Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"enabled": false
},
{
"matchPackagePatterns": ["^com.fasterxml.jackson"],
"enabled": false
"matchDatasources": ["pypi"],
"matchFileNames": [".github/google-play-api/requirements.txt"],
"automerge": true
},
{
"matchUpdateTypes": ["major"],
Expand Down
7 changes: 4 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
set dotenv-load := true
set positional-arguments := true
gradlec := "./project/gradlew -p project --scan"

default:
@just --list

gradle *args='':
{{gradlec}} $@

build:
{{gradlec}} assembleDebug

Expand All @@ -22,9 +26,6 @@ small-espresso:
single-espresso:
{{gradlec}} clean createGmsDebugCoverageReport -Pandroid.testInstrumentationRunnerArguments.annotation=org.owntracks.android.testutils.JustThisTestPlease

format:
{{gradlec}} app:ktfmtFormat

tasks:
{{gradlec}} tasks --all

Expand Down
7 changes: 2 additions & 5 deletions project/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
kotlin("kapt")
alias(libs.plugins.ktfmt)
alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.plugin.serialization)
}

apply<EspressoMetadataEmbeddingPlugin>()
Expand Down Expand Up @@ -235,13 +236,12 @@ dependencies {

// Utility libraries
implementation(libs.bundles.hilt)
implementation(libs.bundles.jackson)
implementation(libs.square.tape2)
implementation(libs.timber)
implementation(libs.bundles.androidx.room)
implementation(libs.bundles.objectbox.migration)
implementation(libs.kotlin.datetime)
implementation(libs.kotlin.serialization)
implementation(libs.kotlinx.serialization.json)

// The BC version shipped under com.android is half-broken. Weird certificate issues etc.
// To solve, we bring in our own version of BC
Expand All @@ -250,9 +250,6 @@ dependencies {
// Widget libraries
implementation(libs.widgets.materialize) { artifact { type = "aar" } }

// These Java EE libs are no longer included in JDKs, so we include explicitly
kapt(libs.bundles.jaxb.annotation.processors)

// Preprocessors
kapt(libs.bundles.kapt.hilt)
ksp(libs.androidx.room.compiler)
Expand Down
19 changes: 4 additions & 15 deletions project/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,12 @@
public protected private *;
}

## Jackson
-keepattributes *Annotation*,EnclosingMethod,Signature
-keepnames class com.fasterxml.jackson.** { *; }
-dontwarn com.fasterxml.jackson.databind.**
-keep class org.codehaus.** { *; }

-keep class com.fasterxml.jackson.databind.ObjectWriter {
public ** writeValueAsString(**);
## Kotlinx.Serialization
-keepclassmembers class ** {
@kotlinx.serialization.Serializable <methods>;
}
-keep class **$$serializer { *; }

-keep class com.fasterxml.jackson.databind.ObjectMapper {
public <methods>;
protected <methods>;
}

# Needed for jackson-module-kotlin
-keep class kotlin.reflect.**

## Paho
-keep class org.eclipse.paho.clent.mqttv3.** { *; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertContains
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.fasterxml.jackson.databind.ObjectMapper
import com.adevinta.android.barista.interaction.BaristaSleepInteractions.sleep
import dagger.hilt.android.testing.HiltAndroidTest
import java.net.ConnectException
import java.net.InetSocketAddress
Expand All @@ -17,6 +17,10 @@ import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.random.Random
import kotlin.time.Duration.Companion.seconds
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import mqtt.broker.Broker
import mqtt.broker.interfaces.Authentication
import org.junit.Test
Expand Down Expand Up @@ -190,8 +194,19 @@ private fun getBroker(
})

@ExperimentalEncodingApi
private fun encodeConfig(config: Map<String, Any>): String =
Base64.encode(ObjectMapper().writeValueAsBytes(config))
private fun encodeConfig(config: Map<String, Any>): String {
val jsonObject = buildJsonObject {
for ((key, value) in config) {
when (value) {
is String -> put(key, value)
is Number -> put(key, value)
is Boolean -> put(key, value)
else -> put(key, value.toString()) // Fallback, might not be correct for all cases
}
}
}
return Base64.encode(Json.encodeToString(jsonObject))
}

private fun getTLSSettings(connectionErrorTest: ConnectionErrorTest): TLSSettings {
val dataBytes = connectionErrorTest.javaClass.getResource("/rootCA.p12")!!.readBytes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertContains
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertNotExist
import com.fasterxml.jackson.databind.ObjectMapper
import dagger.hilt.android.testing.HiltAndroidTest
import java.io.File
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.double
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
Expand Down Expand Up @@ -106,60 +112,63 @@ class LoadActivityTests : TestWithAnActivity<LoadActivity>(false) {
}"""

private fun assertExpectedConfig(input: String) {
val json = ObjectMapper().readTree(input)
assertTrue(json.isObject)
assertEquals("configuration", json["_type"].asText())
assertEquals(2, json["waypoints"].size())
assertEquals("work", json["waypoints"][0]["desc"].asText())
assertEquals(51.5, json["waypoints"][0]["lat"].asDouble(), 0.0001)
assertEquals(-0.02, json["waypoints"][0]["lon"].asDouble(), 0.0001)
assertEquals(150, json["waypoints"][0]["rad"].asInt())
assertEquals(1505910709000, json["waypoints"][0]["tst"].asLong())
assertEquals("home", json["waypoints"][1]["desc"].asText())
assertEquals(53.6, json["waypoints"][1]["lat"].asDouble(), 0.0001)
assertEquals(-1.5, json["waypoints"][1]["lon"].asDouble(), 0.0001)
assertEquals(100, json["waypoints"][1]["rad"].asInt())
assertEquals(1558351273, json["waypoints"][1]["tst"].asLong())
assertTrue(json["auth"].asBoolean())
assertTrue(json["autostartOnBoot"].asBoolean())
assertFalse(json["cleanSession"].asBoolean())
assertEquals("emulator", json["clientId"].asText())
assertTrue(json["cmd"].asBoolean())
assertEquals(34, json["connectionTimeoutSeconds"].asInt())
assertTrue(json["debugLog"].asBoolean())
assertEquals("testdevice", json["deviceId"].asText())
assertFalse(json["enableMapRotation"].asBoolean())
assertTrue(json["fusedRegionDetection"].asBoolean())
assertTrue(json["geocodeEnabled"].asBoolean())
assertEquals("testhost.example.com", json["host"].asText())
assertEquals(150, json["ignoreInaccurateLocations"].asInt())
assertEquals(0, json["ignoreStaleLocations"].asInt())
assertEquals(900, json["keepalive"].asInt())
assertEquals(5, json["locatorDisplacement"].asInt())
assertEquals(60, json["locatorInterval"].asInt())
assertEquals(0, json["mode"].asInt())
assertEquals(1, json["monitoring"].asInt())
assertEquals(10, json["moveModeLocatorInterval"].asInt())
assertEquals(3, json["mqttProtocolLevel"].asInt())
assertFalse(json["notificationHigherPriority"].asBoolean())
assertTrue(json["notificationLocation"].asBoolean())
assertEquals("", json["opencageApiKey"].asText())
assertEquals(3.352, json["osmTileScaleFactor"].asDouble(), 0.0001)
assertEquals("password", json["password"].asText())
assertEquals(30, json["ping"].asInt())
assertEquals(1883, json["port"].asInt())
assertTrue(json["extendedData"].asBoolean())
assertEquals(1, json["pubQos"].asInt())
assertTrue(json["pubRetain"].asBoolean())
assertEquals("owntracks/%u/%d", json["pubTopicBase"].asText())
assertTrue(json["remoteConfiguration"].asBoolean())
assertTrue(json["sub"].asBoolean())
assertEquals(2, json["subQos"].asInt())
assertEquals("owntracks/+/+", json["subTopic"].asText())
assertFalse(json["tls"].asBoolean())
assertTrue(json["usePassword"].asBoolean())
assertEquals("username", json["username"].asText())
assertFalse(json["ws"].asBoolean())
val json = Json.parseToJsonElement(input).jsonObject
assertEquals("configuration", json["_type"]!!.jsonPrimitive.content)
assertEquals(2, json["waypoints"]!!.jsonArray.size)
assertEquals(
"work", json["waypoints"]!!.jsonArray[0].jsonObject["desc"]!!.jsonPrimitive.content)
assertEquals(
51.5, json["waypoints"]!!.jsonArray[0].jsonObject["lat"]!!.jsonPrimitive.double, 0.0001)
assertEquals(
-0.02, json["waypoints"]!!.jsonArray[0].jsonObject["lon"]!!.jsonPrimitive.double, 0.0001)
assertEquals(150, json["waypoints"]!!.jsonArray[0].jsonObject["rad"]!!.jsonPrimitive.int)
assertEquals(
1505910709000, json["waypoints"]!!.jsonArray[0].jsonObject["tst"]!!.jsonPrimitive.long)
assertEquals(
"home", json["waypoints"]!!.jsonArray[1].jsonObject["desc"]!!.jsonPrimitive.content)
assertEquals(
53.6, json["waypoints"]!!.jsonArray[1].jsonObject["lat"]!!.jsonPrimitive.double, 0.0001)
assertEquals(
-1.5, json["waypoints"]!!.jsonArray[1].jsonObject["lon"]!!.jsonPrimitive.double, 0.0001)
assertEquals(100, json["waypoints"]!!.jsonArray[1].jsonObject["rad"]!!.jsonPrimitive.int)
assertEquals(
1558351273, json["waypoints"]!!.jsonArray[1].jsonObject["tst"]!!.jsonPrimitive.long)
assertTrue(json["auth"]!!.jsonPrimitive.boolean)
assertTrue(json["autostartOnBoot"]!!.jsonPrimitive.boolean)
assertFalse(json["cleanSession"]!!.jsonPrimitive.boolean)
assertEquals("emulator", json["clientId"]!!.jsonPrimitive.content)
assertTrue(json["cmd"]!!.jsonPrimitive.boolean)
assertEquals(34, json["connectionTimeoutSeconds"]!!.jsonPrimitive.int)
assertTrue(json["debugLog"]!!.jsonPrimitive.boolean)
assertEquals("testdevice", json["deviceId"]!!.jsonPrimitive.content)
assertFalse(json["enableMapRotation"]!!.jsonPrimitive.boolean)
assertTrue(json["fusedRegionDetection"]!!.jsonPrimitive.boolean)
assertTrue(json["geocodeEnabled"]!!.jsonPrimitive.boolean)
assertEquals("testhost.example.com", json["host"]!!.jsonPrimitive.content)
assertEquals(150, json["ignoreInaccurateLocations"]!!.jsonPrimitive.int)
assertEquals(0, json["ignoreStaleLocations"]!!.jsonPrimitive.int)
assertEquals(900, json["keepalive"]!!.jsonPrimitive.int)
assertEquals(5, json["locatorDisplacement"]!!.jsonPrimitive.int)
assertEquals(60, json["locatorInterval"]!!.jsonPrimitive.int)
assertEquals(0, json["mode"]!!.jsonPrimitive.int)
assertEquals(1, json["monitoring"]!!.jsonPrimitive.int)
assertEquals(10, json["moveModeLocatorInterval"]!!.jsonPrimitive.int)
assertEquals(3, json["mqttProtocolLevel"]!!.jsonPrimitive.int)
assertFalse(json["notificationHigherPriority"]!!.jsonPrimitive.boolean)
assertTrue(json["notificationLocation"]!!.jsonPrimitive.boolean)
assertEquals("", json["opencageApiKey"]!!.jsonPrimitive.content)
assertEquals(3.352, json["osmTileScaleFactor"]!!.jsonPrimitive.double, 0.0001)
assertEquals("password", json["password"]!!.jsonPrimitive.content)
assertEquals(30, json["ping"]!!.jsonPrimitive.int)
assertEquals(1883, json["port"]!!.jsonPrimitive.int)
assertTrue(json["extendedData"]!!.jsonPrimitive.boolean)
assertEquals(1, json["pubQos"]!!.jsonPrimitive.int)
assertTrue(json["pubRetain"]!!.jsonPrimitive.boolean)
assertEquals("owntracks/%u/%d", json["pubTopicBase"]!!.jsonPrimitive.content)
assertTrue(json["remoteConfiguration"]!!.jsonPrimitive.boolean)
assertTrue(json["sub"]!!.jsonPrimitive.boolean)
assertEquals(2, json["subQos"]!!.jsonPrimitive.int)
assertEquals("owntracks/+/+", json["subTopic"]!!.jsonPrimitive.content)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assert
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawer
import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo
import com.fasterxml.jackson.databind.ObjectMapper
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
Expand Down Expand Up @@ -181,7 +182,8 @@ class WaypointsActivityTests :
clickOn(R.string.title_activity_preferences)
clickOn(R.string.configurationManagement)
val effectiveConfiguration = getText(onView(withId(R.id.effectiveConfiguration)))
val json = ObjectMapper().readTree(effectiveConfiguration)

val json = Json.parseToJsonElement(effectiveConfiguration).jsonObject
assertTrue(json.isObject)
assertTrue(json.has("waypoints"))
assertEquals(1, json["waypoints"].size())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.owntracks.android.ui.map

import androidx.databinding.ViewDataBinding
import kotlinx.serialization.Serializable
import org.owntracks.android.R
import org.owntracks.android.preferences.types.FromConfiguration
import org.owntracks.android.ui.map.osm.OSMMapFragment

@Serializable
enum class MapLayerStyle {
GoogleMapDefault,
GoogleMapHybrid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@ package org.owntracks.android.geocoding
import android.content.Context
import android.location.Address
import java.math.BigDecimal
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.Locale
import kotlin.time.Duration.Companion.minutes
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.owntracks.android.location.LatLng
import timber.log.Timber

class DeviceGeocoder internal constructor(context: Context) : CachingGeocoder() {
private val geocoder: android.location.Geocoder =
android.location.Geocoder(context, Locale.getDefault())
private var tripResetTimestamp: Instant = Instant.MIN
private var tripResetTimestamp: Instant = Instant.DISTANT_PAST

override suspend fun reverse(latLng: LatLng): GeocodeResult {
return if (geocoderAvailable()) {
super.reverse(latLng)
} else {
tripResetTimestamp = Instant.now().plus(1, ChronoUnit.MINUTES)
tripResetTimestamp = Clock.System.now().plus(1.minutes)
GeocodeResult.Fault.Unavailable(tripResetTimestamp)
}
}

override fun doLookup(latitude: BigDecimal, longitude: BigDecimal): GeocodeResult {
if (tripResetTimestamp > Instant.now()) {
if (tripResetTimestamp > Clock.System.now()) {
Timber.w("Rate-limited, not querying until $tripResetTimestamp")
return GeocodeResult.Fault.RateLimited(tripResetTimestamp)
}
Expand All @@ -41,7 +42,7 @@ class DeviceGeocoder internal constructor(context: Context) : CachingGeocoder()
GeocodeResult.Empty
}
} catch (e: Exception) {
tripResetTimestamp = Instant.now().plus(1, ChronoUnit.MINUTES)
tripResetTimestamp = Clock.System.now().plus(1.minutes)
GeocodeResult.Fault.Error(e.toString(), tripResetTimestamp)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.owntracks.android.geocoding

import java.time.Instant
import kotlinx.datetime.Instant

sealed class GeocodeResult {
data class Formatted(val text: String) : GeocodeResult()
Expand Down
Loading
Loading