commit 0f30f0206f13554d60091d272fc35cf5954cbe01 Author: Jakob Dalsgaard Date: Wed Nov 22 13:43:26 2023 +0100 First Commit of WearSignalK2 diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..ad4cfc2 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,81 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "net.dalsgaard.wearsignalk" + compileSdk = 33 + + defaultConfig { + applicationId = "net.dalsgaard.wearsignalk" + minSdk = 30 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + vectorDrawables { + useSupportLibrary = true + } + + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.3" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("com.google.android.gms:play-services-wearable:18.1.0") + implementation("androidx.percentlayout:percentlayout:1.0.0") + implementation("androidx.legacy:legacy-support-v4:1.0.0") + implementation("androidx.recyclerview:recyclerview:1.3.1") + implementation(platform("androidx.compose:compose-bom:2023.03.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.wear.compose:compose-material:1.0.0") + implementation("androidx.wear.compose:compose-foundation:1.0.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1") + implementation("androidx.activity:activity-compose:1.5.1") + androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-tooling") + debugImplementation("androidx.compose.ui:ui-test-manifest") + + // websocket functionality + implementation("io.ktor:ktor-client-core:2.3.4") + implementation("io.ktor:ktor-client-cio:2.3.4") + implementation("io.ktor:ktor-client-websockets:2.3.4") + + // jsonpath functionality + implementation("com.nfeld.jsonpathkt:jsonpathkt:2.0.1") + + // kotlin flow functionality for state propagation + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2") + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8d52564 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/connection/SignalKConnection.kt b/app/src/main/java/net/dalsgaard/wearsignalk/connection/SignalKConnection.kt new file mode 100644 index 0000000..b749860 --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/connection/SignalKConnection.kt @@ -0,0 +1,215 @@ +package net.dalsgaard.wearsignalk.connection + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.fasterxml.jackson.databind.JsonNode +import com.nfeld.jsonpathkt.JsonPath +import com.nfeld.jsonpathkt.extension.read +import io.ktor.client.HttpClient +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.websocket.WebSockets +import io.ktor.client.plugins.websocket.webSocket +import io.ktor.http.HttpMethod +import io.ktor.websocket.send +import io.ktor.websocket.Frame +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.runBlocking +import java.time.Instant +import kotlin.concurrent.thread + +private const val TAG = "SignalKConnection" + +private const val SUBSCRIPTION = "{\n" + + " \"context\": \"vessels.self\",\n" + + " \"subscribe\": [\n" + + " {\n" + + " \"path\": \"navigation.datetime\",\n" + + " \"period\": 5000\n" + + " },\n" + + " {\n" + + " \"path\": \"navigation.courseRhumbline.nextPoint\",\n" + + " \"period\": 5000\n" + + " },\n" + + " {\n" + + " \"path\": \"navigation.courseOverGroundTrue\",\n" + + " \"period\": 5000\n" + + " },\n" + + " {\n" + + " \"path\": \"navigation.position\",\n" + + " \"period\": 5000\n" + + " },\n" + + " {\n" + + " \"path\": \"navigation.speedOverGround\",\n" + + " \"period\": 5000\n" + + " }\n" + + " ]\n" + + "}\n" + + + + +public class KDataFlow(inflow: Channel, default: T): ViewModel() { + val data = flow { while(true) { emit(inflow.receive()) } } + .stateIn(viewModelScope, SharingStarted.Lazily, default) +} + + +data class Position (val latitude: Double = 0.0, val longitude: Double = 0.0) + +/** + * Interface for SignalK data values to KData structure mapping + */ +interface SignalKMapper { + suspend fun update(json : JsonNode) : Boolean +} + +// navigation.datetime +class NavigationDatetimeMapper(val outflow: Channel) : SignalKMapper { + override suspend fun update(json: JsonNode): Boolean { + val tstamputc = json?.read("value") + val t = Instant.parse(tstamputc) + outflow.send(t) + return true + } +} + +// navigation.courseRhumbline.nextPoint +class NavigationCourseRhumblineNextPointMapper : SignalKMapper { + override suspend fun update(json: JsonNode): Boolean { + val point = Pair(json?.read("value.latitude"), json?.read("value.longitude")) +/* + data.nextPointLatitude = point?.first + data.nextPointLongitude = point?.second + + data.nextPointBearingTrue = json?.read("bearingTrue.value") + data.nextPointVMG = json?.read("velocityMadeGood.value") + data.nextPointDistance = json?.read("distance.value") +*/ + return true + } +} + +class NavigationPositionMapper(val outflow: Channel) : SignalKMapper { + override suspend fun update(json: JsonNode): Boolean { + val point = Pair(json?.read("value.latitude"), json?.read("value.longitude")) + if (point.first != null && point.second != null) { + outflow.send(Position(point.first!!, point.second!!)) + return true + } else return false + } +} + +class NavigationSpeedOverGroundMapper(val outflow: Channel) : SignalKMapper { + override suspend fun update(json: JsonNode): Boolean { + val speedOverGround = json?.read("value") + if (speedOverGround != null) outflow.send(speedOverGround) + return true + } +} + +class NavigationCourseOverGroundMapper(val outflow: Channel) : SignalKMapper { + override suspend fun update(json: JsonNode): Boolean { + val courseOverGroundTrue = json?.read("value") + if (courseOverGroundTrue != null) outflow.send(courseOverGroundTrue) + return true + } +} + + + + + +class SignalKConnection { + + private val ws_url = "ws://nora.dalsgaard.net:3000/signalk/v1/stream?subscribe=none" + + private var bgThread: Thread? = null; + private var keepRunning : Boolean = true + + val navigationPositionChannel = Channel() + val datetimeChannel = Channel() + val courseOverGroundTrueChannel = Channel() + val speedOverGroundChannel = Channel() + + private val signalKmapper : Map + init { + signalKmapper = mapOf( + "navigation.datetime" to NavigationDatetimeMapper(datetimeChannel), + "navigation.courseOverGroundTrue" to NavigationCourseOverGroundMapper(courseOverGroundTrueChannel), + "navigation.speedOverGround" to NavigationSpeedOverGroundMapper(speedOverGroundChannel), + "navigation.position" to NavigationPositionMapper(navigationPositionChannel), + "navigation.courseRhumbline.nextPoint" to NavigationCourseRhumblineNextPointMapper() + ) + + } + + public fun getFlowPosition () : KDataFlow { + return KDataFlow(navigationPositionChannel, Position()) + } + + public fun getFlowDatetime () : KDataFlow { + return KDataFlow(datetimeChannel, Instant.ofEpochMilli(0)) + } + + public fun getFlowCourseOverGround () : KDataFlow { + return KDataFlow(courseOverGroundTrueChannel, 0.0) + } + + public fun getFlowSpeedOverGround () : KDataFlow { + return KDataFlow(speedOverGroundChannel, 0.0) + } + + + public fun stop () { + keepRunning = false + } + + public fun start () { + this.bgThread = thread(start = true, isDaemon = true) { + while (keepRunning) { + Log.d(TAG, "Inside ConnectionThread") + val client = HttpClient { + install(WebSockets) + install(HttpTimeout) { + connectTimeoutMillis = 10_000 + requestTimeoutMillis = 10_000 + } + } + runBlocking { + client.webSocket( + method = HttpMethod.Get, + host = "nora.dalsgaard.net", + port = 3000, + path = "/signalk/v1/stream?subscribe=none" + ) { + // get welcome message + val msg = incoming.receive().data.decodeToString() + Log.d(TAG, "Welcome message: $msg") + send(SUBSCRIPTION) + Log.d(TAG, "sent: $SUBSCRIPTION") + while (keepRunning) { + val othersMessage = incoming.receive() as? Frame.Text ?: continue + val body = othersMessage.data.decodeToString() + val json = JsonPath.parse(body) + val updates = json?.read>("$.updates[*].values[*]") + if (updates != null) { + Log.d(TAG, "Got updates") + for (i in updates) { + val path = i.read("path") + signalKmapper[path]?.update(i) + } + } + // Log.d(TAG, "data is now: $data") + } + Log.d(TAG, "closing websocket") + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/presentation/MainActivity.kt b/app/src/main/java/net/dalsgaard/wearsignalk/presentation/MainActivity.kt new file mode 100644 index 0000000..a82d4b5 --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/presentation/MainActivity.kt @@ -0,0 +1,254 @@ +/* While this template provides a good starting point for using Wear Compose, you can always + * take a look at https://github.com/android/wear-os-samples/tree/main/ComposeStarter and + * https://github.com/android/wear-os-samples/tree/main/ComposeAdvanced to find the most up to date + * changes to the libraries and their usages. + */ + +package net.dalsgaard.wearsignalk.presentation + +import android.graphics.drawable.Drawable +import android.util.Log +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.draw.scale +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.zIndex +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.wear.compose.material.MaterialTheme +import androidx.wear.compose.material.Text +import net.dalsgaard.wearsignalk.R +import net.dalsgaard.wearsignalk.connection.KDataFlow +import net.dalsgaard.wearsignalk.connection.Position +import net.dalsgaard.wearsignalk.connection.SignalKConnection +import net.dalsgaard.wearsignalk.presentation.theme.WearSignalKTheme +import net.dalsgaard.wearsignalk.util.Design +import net.dalsgaard.wearsignalk.util.Vector +import net.dalsgaard.wearsignalk.util.awd_ident +import net.dalsgaard.wearsignalk.util.cog_ident +import net.dalsgaard.wearsignalk.util.compass_rose +import net.dalsgaard.wearsignalk.util.eyeMatrix +import net.dalsgaard.wearsignalk.util.forSquareScreen +import net.dalsgaard.wearsignalk.util.latToString +import net.dalsgaard.wearsignalk.util.lonToString +import net.dalsgaard.wearsignalk.util.rotMatrix +import net.dalsgaard.wearsignalk.util.scaleMatrix +import net.dalsgaard.wearsignalk.util.ship +import java.time.Instant +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import kotlin.math.cos +import kotlin.math.min +import kotlin.math.round +import kotlin.math.sin + +private const val TAG = "MainActivity" + +class MainActivity : ComponentActivity() { + + var conn = SignalKConnection() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(TAG, "onCreate in MainActivity") + conn.start() + + setContent { +// WearApp("Android", conn.getKData()) + WearApp("Android", conn) + } + } +} + +fun drawDesign(design: Design, dr: DrawScope) { + for (t in design.traces) { + var idx_iterator = t.indices.iterator() + var last_idx = idx_iterator.next() + while (idx_iterator.hasNext()) { + val idx = idx_iterator.next() + dr.drawLine(t.color, start=Offset(design.points[last_idx].x, design.points[last_idx].y), + end=Offset(design.points[idx].x, design.points[idx].y), strokeWidth = t.width) + last_idx = idx + } + } +} + +@Composable +fun WearApp(greetingName: String, conn: SignalKConnection) { + + val cog_true_flow = conn.getFlowCourseOverGround() + WearSignalKTheme { + /* If you have enough items in your list, use [ScalingLazyColumn] which is an optimized + * version of LazyColumn for wear devices with some added features. For more information, + * see d.android.com/wear/compose. + */ + ship(conn.getFlowCourseOverGround()) + + + Column (modifier = Modifier + .fillMaxSize() + .background(color = Color.Transparent) + .drawBehind + { + val min_dim = min(size.width, size.height) + val ship_ship = forSquareScreen(ship, min_dim) + drawDesign(ship_ship, this) + + }, verticalArrangement = Arrangement.Center) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { Time(conn.getFlowDatetime()) + } + Row (modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Column (modifier = Modifier, verticalArrangement = Arrangement.Center,Alignment.CenterHorizontally) { + courseOverGroundTrue(conn.getFlowCourseOverGround()) + } + Column (modifier = Modifier, verticalArrangement = Arrangement.Center) { + speedOverGround(conn.getFlowSpeedOverGround()) + } + + } + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Position(conn.getFlowPosition()) + //Text("JD") + //Text(latToString(data.latitude!!)) + //Text(lonToString(data.longitude!!)) + } + } + } +} + +@Composable +fun WearAppPreview(greetingName: String) { + WearSignalKTheme { + /* If you have enough items in your list, use [ScalingLazyColumn] which is an optimized + * version of LazyColumn for wear devices with some added features. For more information, + * see d.android.com/wear/compose. + */ + Column( + modifier = Modifier, + verticalArrangement = Arrangement.Center + ) { + val data = Position(12.2, 55.2) + Column (modifier = Modifier, verticalArrangement = Arrangement.Center,Alignment.CenterHorizontally) { + + Text("Boat Position Pre", Modifier.scale(0.8F)) + Text(latToString(data.latitude!!)) + Text(lonToString(data.longitude!!)) + } + } + } +} + +@Composable +fun ship(kdataflow : KDataFlow) { + val cog_rad by kdataflow.data.collectAsStateWithLifecycle() + Canvas(modifier = Modifier.fillMaxSize().background(Color.Transparent).zIndex(-1F)) { + val min_dim = min(size.width, size.height) + val true_heading = 0F // LocalTime.now().second * 6.0F + val heading = -(true_heading / 360.0 * 2.0 * Math.PI).toFloat() + val heading_rotate_matrix = rotMatrix(heading) + val compass = forSquareScreen(heading_rotate_matrix * compass_rose, min_dim) + drawDesign(compass, this) + + // val cog = (2.0 * true_heading/360 * 2.0 * Math.PI).toFloat() + + val cog = cog_rad.toFloat() + Log.d(TAG, "got cog to " + (cog * 180 / Math.PI)) + val cog_rotate_matrix = rotMatrix(-cog.toFloat() + heading) + val cog_pointer = forSquareScreen(cog_rotate_matrix * cog_ident, min_dim) + drawDesign(cog_pointer, this) + + val awd = (Math.PI/8*13).toFloat() // (1/2 * Math.PI).toFloat() + val awd_rotate_matrix = rotMatrix(-awd + heading) + val awd_pointer = forSquareScreen(awd_rotate_matrix * awd_ident, min_dim) + drawDesign(awd_pointer, this) + + } +} + + +@Composable +fun courseOverGroundTrue(kdataflow : KDataFlow) { + val deg by kdataflow.data.collectAsStateWithLifecycle() + val deg360 = round(deg*180.0/Math.PI) + Text("COG", Modifier.scale(0.8F)) + Text("%03.0f°".format(deg360)) +} + +@Composable +fun speedOverGround(kdataflow : KDataFlow) { + val spd by kdataflow.data.collectAsStateWithLifecycle() + val spd_kn = spd * 1.94384 + Text("SOG", Modifier.scale(0.8F)) + Text("%02.1fkn".format(spd_kn)) +} + +@Composable +fun Position(kdataflow : KDataFlow) { + val pos by kdataflow.data.collectAsStateWithLifecycle() + Column (modifier = Modifier.scale(0.8F), verticalArrangement = Arrangement.Center,Alignment.CenterHorizontally) { + + Text(latToString(pos.latitude)) + Text(lonToString(pos.longitude)) +// Text("Boat Position", Modifier.scale(0.8F)) + } +} + +@Composable +fun Time(kdataflow : KDataFlow) { + val time by kdataflow.data.collectAsStateWithLifecycle() + val timez = OffsetDateTime.ofInstant(time, ZoneOffset.UTC) + val fmt1 = DateTimeFormatter.ofPattern("MMM dd HH:mm") + + Column (modifier = Modifier.scale(0.8F), verticalArrangement = Arrangement.Center,Alignment.CenterHorizontally) { + // Text("Boat Time", Modifier.scale(0.8F)) + Text(timez.format(fmt1)) + } +} + +@Composable +fun Greeting(greetingName: String) { + Text( + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + color = MaterialTheme.colors.primary, + text = stringResource(R.string.hello_world, greetingName) + ) +} + +@Preview(device = Devices.WEAR_OS_SMALL_ROUND, showSystemUi = true) +@Composable +fun DefaultPreview() { + WearAppPreview("Preview Android") +} \ No newline at end of file diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/util/degrees.kt b/app/src/main/java/net/dalsgaard/wearsignalk/util/degrees.kt new file mode 100644 index 0000000..104bed7 --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/util/degrees.kt @@ -0,0 +1,34 @@ +package net.dalsgaard.wearsignalk.util + +import kotlin.math.floor + +private fun degToString (degrees: Double, dir : Char) : String { + + val deg = floor(degrees) + val frac60 = 60 * (degrees - deg) + val min = floor(frac60) + val frac3600 = 60 * (frac60 - min) + val sec = floor(frac3600) + + return "%03.0f°%02.0fʹ%02.0fʺ %s".format(deg, min, sec, dir) +} + +public fun latToString (latitude: Double) : String { + return if (latitude > 0) { + degToString(latitude, 'E') + } else if (latitude < 0) { + degToString(-latitude, 'W') + } else { + "000°00ʹ00ʺ -" + } +} + +public fun lonToString (longitude: Double) : String { + return if (longitude > 0) { + degToString(longitude, 'N') + } else if (longitude < 0) { + degToString(-longitude, 'S') + } else { + "000°00ʹ00ʺ -" + } +} diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/util/design.kt b/app/src/main/java/net/dalsgaard/wearsignalk/util/design.kt new file mode 100644 index 0000000..476f8d3 --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/util/design.kt @@ -0,0 +1,96 @@ +package net.dalsgaard.wearsignalk.util + +import androidx.compose.ui.graphics.Color + +val compass_rose = + Design(listOf(Vector(0.0F, 0.5F), Vector(0.0F, 0.35F)), listOf(Design.Trace(listOf(0, 1), 5.0F, Color.Red))) + + Design(listOf(Vector(0.020869565217391303F, 0.2582608695652174F), Vector(0.02086956521739131F, 0.331304347826087F), + Vector(0.017391304347826084F, 0.2582608695652174F), Vector(-0.024347826086956518F, 0.3243478260869565F), + Vector(0.017391304347826084F, 0.2652173913043478F), Vector(-0.024347826086956518F, 0.331304347826087F), + Vector(-0.024347826086956525F, 0.2582608695652174F), Vector(-0.024347826086956518F, 0.331304347826087F), + Vector(0.03130434782608695F, 0.2582608695652174F), Vector(0.017391304347826084F, 0.2582608695652174F), + Vector(-0.013913043478260875F, 0.2582608695652174F), Vector(-0.03478260869565218F, 0.2582608695652174F), + Vector(0.031304347826086966F, 0.3313043478260869F), Vector(0.010434782608695656F, 0.331304347826087F)), + listOf(Design.Trace(listOf(0, 1), 2F, Color.Red), + Design.Trace(listOf(2, 3), 2F, Color.Red), + Design.Trace(listOf(4, 5), 2F, Color.Red), + Design.Trace(listOf(6, 7), 2F, Color.Red), + Design.Trace(listOf(8, 9), 2F, Color.Red), + Design.Trace(listOf(10, 11), 2F, Color.Red), + Design.Trace(listOf(12, 13), 2F, Color.Red))) + + Design(listOf(Vector(0.5F, 0.0F), Vector(0.35F, 0.0F)), listOf(Design.Trace(listOf(0, 1), 5.0F, Color.Gray))) + + Design(listOf(Vector(0.0F, -0.5F), Vector(0.0F, -0.35F)), listOf(Design.Trace(listOf(0, 1), 5.0F, Color.Gray))) + + Design(listOf(Vector(-0.5F, 0.0F), Vector(-0.35F, 0.0F)), listOf(Design.Trace(listOf(0, 1), 5.0F, Color.Gray))) + + Design(listOf(Vector(0.3535533905932738F, 0.35355339059327373F), Vector(0.3005203820042827F, 0.30052038200428266F)), listOf(Design.Trace(listOf(0, 1), 3.0F, Color.Gray))) + + Design(listOf(Vector(-0.35355339059327373F, 0.3535533905932738F), Vector(-0.30052038200428266F, 0.3005203820042827F)), listOf(Design.Trace(listOf(0, 1), 3.0F, Color.Gray))) + + Design(listOf(Vector(-0.35355339059327384F, -0.35355339059327373F), Vector(-0.30052038200428277F, -0.30052038200428266F)), listOf(Design.Trace(listOf(0, 1), 3.0F, Color.Gray))) + + Design(listOf(Vector(0.3535533905932737F, -0.35355339059327384F), Vector(0.3005203820042826F, -0.30052038200428277F)), listOf(Design.Trace(listOf(0, 1), 3.0F, Color.Gray))) + + Design(listOf(Vector(0.4903926402016152F, 0.09754516100806412F), Vector(0.4413533761814537F, 0.08779064490725771F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.46193976625564337F, 0.1913417161825449F), Vector(0.415745789630079F, 0.1722075445642904F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.4157348061512726F, 0.2777851165098011F), Vector(0.37416132553614534F, 0.250006604858821F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.27778511650980114F, 0.4157348061512726F), Vector(0.25000660485882104F, 0.37416132553614534F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.19134171618254492F, 0.46193976625564337F), Vector(0.17220754456429044F, 0.415745789630079F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.09754516100806417F, 0.4903926402016152F), Vector(0.08779064490725776F, 0.4413533761814537F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.0975451610080641F, 0.4903926402016152F), Vector(-0.08779064490725769F, 0.4413533761814537F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.19134171618254486F, 0.46193976625564337F), Vector(-0.17220754456429038F, 0.415745789630079F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.277785116509801F, 0.41573480615127273F), Vector(-0.25000660485882087F, 0.37416132553614545F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.4157348061512727F, 0.2777851165098011F), Vector(-0.3741613255361454F, 0.250006604858821F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.46193976625564337F, 0.19134171618254495F), Vector(-0.415745789630079F, 0.17220754456429047F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.4903926402016152F, 0.0975451610080643F), Vector(-0.4413533761814537F, 0.08779064490725788F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.4903926402016152F, -0.09754516100806418F), Vector(-0.4413533761814537F, -0.08779064490725777F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.4619397662556434F, -0.19134171618254484F), Vector(-0.4157457896300791F, -0.17220754456429035F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.41573480615127273F, -0.277785116509801F), Vector(-0.37416132553614545F, -0.25000660485882087F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.2777851165098011F, -0.4157348061512726F), Vector(-0.250006604858821F, -0.37416132553614534F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.19134171618254517F, -0.46193976625564326F), Vector(-0.17220754456429066F, -0.41574578963007897F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(-0.09754516100806433F, -0.49039264020161516F), Vector(-0.0877906449072579F, -0.44135337618145365F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.09754516100806415F, -0.4903926402016152F), Vector(0.08779064490725774F, -0.4413533761814537F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.191341716182545F, -0.4619397662556433F), Vector(0.1722075445642905F, -0.41574578963007897F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.2777851165098009F, -0.41573480615127273F), Vector(0.2500066048588208F, -0.37416132553614545F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.4157348061512726F, -0.2777851165098011F), Vector(0.37416132553614534F, -0.250006604858821F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.46193976625564326F, -0.1913417161825452F), Vector(0.41574578963007897F, -0.1722075445642907F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(listOf(Vector(0.49039264020161516F, -0.09754516100806436F), Vector(0.44135337618145365F, -0.08779064490725792F)), listOf(Design.Trace(listOf(0, 1), 1.0F, Color.Gray))) + + Design(emptyList(), emptyList()) + +val cog_ident_color = Color(0xAD, 0xD8, 0xE6) +val cog_ident = + Design(listOf(Vector(0.0F, 0.5F), Vector(0.0F, 0.4F), + Vector(-0.009937500000000002F, 0.3125F), Vector(-0.013062500000000001F, 0.3109375F), Vector(-0.016187500000000004F, 0.3078125F), Vector(-0.017750000000000005F, 0.3046875F), Vector(-0.017750000000000005F, 0.29843749999999997F), Vector(-0.016187500000000004F, 0.2953125F), Vector(-0.013062500000000005F, 0.2921875F), Vector(-0.009937500000000005F, 0.29062499999999997F), Vector(-0.005250000000000004F, 0.2890625F), Vector(0.002562499999999996F, 0.2890625F), Vector(0.007249999999999997F, 0.29062499999999997F), Vector(0.010374999999999999F, 0.2921875F), Vector(0.013499999999999998F, 0.2953125F), Vector(0.015062499999999996F, 0.29843749999999997F), Vector(0.015062499999999996F, 0.3046875F), Vector(0.013499999999999998F, 0.3078125F), Vector(0.010374999999999999F, 0.3109375F), Vector(0.007249999999999998F, 0.3125F), + Vector(-0.017750000000000002F, 0.32968749999999997F), Vector(-0.0161875F, 0.3265625F), Vector(-0.013062500000000001F, 0.3234375F), Vector(-0.009937500000000002F, 0.32187499999999997F), Vector(-0.005250000000000002F, 0.3203125F), Vector(0.0025624999999999984F, 0.3203125F), Vector(0.007249999999999999F, 0.32187499999999997F), Vector(0.010374999999999999F, 0.3234375F), Vector(0.013499999999999998F, 0.3265625F), Vector(0.0150625F, 0.32968749999999997F), Vector(0.0150625F, 0.3359375F), Vector(0.013500000000000002F, 0.3390625F), Vector(0.010375000000000002F, 0.3421875F), Vector(0.00725F, 0.34375F), Vector(0.0025624999999999997F, 0.34531249999999997F), Vector(-0.00525F, 0.34531249999999997F), Vector(-0.009937500000000002F, 0.34375F), Vector(-0.013062500000000001F, 0.3421875F), Vector(-0.0161875F, 0.3390625F), Vector(-0.017750000000000002F, 0.3359375F), Vector(-0.017750000000000002F, 0.32968749999999997F), + Vector(-0.009937499999999998F, 0.37968749999999996F), Vector(-0.013062499999999998F, 0.378125F), Vector(-0.0161875F, 0.375F), Vector(-0.017750000000000002F, 0.37187499999999996F), Vector(-0.017750000000000002F, 0.365625F), Vector(-0.0161875F, 0.3625F), Vector(-0.013062500000000001F, 0.359375F), Vector(-0.009937500000000002F, 0.3578125F), Vector(-0.00525F, 0.35624999999999996F), Vector(0.0025625000000000005F, 0.35624999999999996F), Vector(0.007250000000000001F, 0.3578125F), Vector(0.010375000000000002F, 0.359375F), Vector(0.013500000000000002F, 0.3625F), Vector(0.015062500000000003F, 0.365625F), Vector(0.015062500000000003F, 0.37187499999999996F), Vector(0.013500000000000002F, 0.375F), Vector(0.010375000000000002F, 0.378125F), Vector(0.007250000000000002F, 0.37968749999999996F), Vector(0.002562500000000002F, 0.37968749999999996F), + Vector(0.0025625000000000014F, 0.37187499999999996F), Vector(0.002562500000000002F, 0.37968749999999996F)), + listOf(Design.Trace(listOf(0, 1), 1.0F, cog_ident_color), + Design.Trace(listOf(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), 1.0F, cog_ident_color), + Design.Trace(listOf(20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40), 1.0F, cog_ident_color), + Design.Trace(listOf(41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59), 1.0F, cog_ident_color), + Design.Trace(listOf(60, 61), 1.0F, cog_ident_color))) + +val awd_ident_color = Color (0xFF, 0xF7, 0x00) +val awd_ident = + Design(listOf(Vector(0.0F, 0.5F), Vector(0.0F, 0.4F), + Vector(-0.018047619047619052F, 0.3F), Vector(0.015285714285714281F, 0.2873015873015873F), + Vector(-0.018047619047619052F, 0.3F), Vector(0.015285714285714284F, 0.3126984126984127F), + Vector(0.004174603174603171F, 0.292063492063492F), Vector(0.004174603174603172F, 0.3079365079365079F), + Vector(-0.018047619047619052F, 0.3126984126984127F), Vector(0.015285714285714284F, 0.3206349206349206F), + Vector(-0.01804761904761905F, 0.32857142857142857F), Vector(0.015285714285714284F, 0.3206349206349206F), + Vector(-0.01804761904761905F, 0.32857142857142857F), Vector(0.015285714285714284F, 0.3365079365079365F), + Vector(-0.01804761904761905F, 0.34444444444444444F), Vector(0.015285714285714284F, 0.3365079365079365F), + Vector(-0.01804761904761905F, 0.3555555555555555F), Vector(0.015285714285714284F, 0.3555555555555555F), + Vector(-0.01804761904761905F, 0.3555555555555555F), Vector(-0.01804761904761905F, 0.36666666666666664F), Vector(-0.01646031746031746F, 0.3714285714285714F), Vector(-0.013285714285714283F, 0.3746031746031746F), Vector(-0.010111111111111109F, 0.3761904761904762F), Vector(-0.0053492063492063474F, 0.37777777777777777F), Vector(0.002587301587301589F, 0.37777777777777777F), Vector(0.007349206349206351F, 0.3761904761904762F), Vector(0.010523809523809526F, 0.3746031746031746F), Vector(0.0136984126984127F, 0.3714285714285714F), Vector(0.015285714285714288F, 0.36666666666666664F), Vector(0.015285714285714284F, 0.3555555555555555F)), + listOf(Design.Trace(listOf(0, 1), 1.0F, awd_ident_color), + Design.Trace(listOf(2, 3), 1.0F, awd_ident_color), + Design.Trace(listOf(4, 5), 1.0F, awd_ident_color), + Design.Trace(listOf(6, 7), 1.0F, awd_ident_color), + Design.Trace(listOf(8, 9), 1.0F, awd_ident_color), + Design.Trace(listOf(10, 11), 1.0F, awd_ident_color), + Design.Trace(listOf(12, 13), 1.0F, awd_ident_color), + Design.Trace(listOf(14, 15), 1.0F, awd_ident_color), + Design.Trace(listOf(16, 17), 1.0F, awd_ident_color), + Design.Trace(listOf(18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29), 1.0F, awd_ident_color))) + +val ship_color=Color(0x80, 0x85, 0x88) +val ship = Design(listOf(Vector(-0.008872458410351202F, -0.4F), Vector(-0.022181146025878007F, -0.39556377079482447F), Vector(-0.03844731977818854F, -0.38669131238447324F), Vector(-0.047319778188539746F, -0.37634011090573016F), Vector(-0.0857670979667283F, -0.24916820702402961F), Vector(-0.10646950092421442F, -0.1604436229205176F), Vector(-0.11386321626617377F, -0.10425138632162663F), Vector(-0.1168207024029575F, -0.04066543438077634F), Vector(-0.11534195933456563F, -0.0007393715341959335F), Vector(-0.11090573012939003F, 0.05841035120147875F), Vector(-0.10055452865064696F, 0.11608133086876156F), Vector(-0.08428835489833643F, 0.18558225508317933F), Vector(-0.051756007393715345F, 0.28170055452865067F), Vector(-0.011829944547134937F, 0.3748613678373383F), Vector(-0.001478743068391867F, 0.4F), Vector(0.001478743068391867F, 0.4F), Vector(0.011829944547134937F, 0.3748613678373383F), Vector(0.051756007393715345F, 0.28170055452865067F), Vector(0.08428835489833643F, 0.18558225508317933F), Vector(0.10055452865064696F, 0.11608133086876156F), Vector(0.11090573012939003F, 0.05841035120147875F), Vector(0.11534195933456563F, -0.0007393715341959335F), Vector(0.1168207024029575F, -0.04066543438077634F), Vector(0.11386321626617377F, -0.10425138632162663F), Vector(0.10646950092421442F, -0.1604436229205176F), Vector(0.0857670979667283F, -0.24916820702402961F), Vector(0.047319778188539746F, -0.37634011090573016F), Vector(0.03844731977818854F, -0.38669131238447324F), Vector(0.022181146025878007F, -0.39556377079482447F), Vector(0.008872458410351202F, -0.4F), Vector(-0.047319778188539746F, -0.37634011090573016F), Vector(-0.031053604436229208F, -0.3792975970425139F), Vector(-0.01478743068391867F, -0.38225508317929763F), Vector(0.01478743068391867F, -0.38225508317929763F), Vector(0.031053604436229208F, -0.3792975970425139F), Vector(0.047319778188539746F, -0.37634011090573016F), Vector(0.0F, -0.3127541589648799F), Vector(-0.002957486136783734F, -0.30683918669131244F), Vector(-0.001478743068391867F, -0.300924214417745F), Vector(0.0F, -0.22994454713493534F), Vector(0.001478743068391867F, -0.300924214417745F), Vector(0.002957486136783734F, -0.30683918669131244F), Vector(-0.002957486136783734F, -0.2935304990757856F), Vector(-0.03992606284658041F, -0.29500924214417745F), Vector(-0.05027726432532348F, -0.2905730129390019F), Vector(-0.05619223659889095F, -0.2802218114602588F), Vector(-0.062107208872458415F, -0.25951940850277266F), Vector(-0.06802218114602589F, -0.22107208872458411F), Vector(-0.07541589648798522F, -0.12643253234750462F), Vector(0.07541589648798522F, -0.12643253234750462F), Vector(0.06802218114602589F, -0.22107208872458411F), Vector(0.062107208872458415F, -0.25951940850277266F), Vector(0.05619223659889095F, -0.2802218114602588F), Vector(0.05027726432532348F, -0.2905730129390019F), Vector(0.03992606284658041F, -0.29500924214417745F), Vector(0.002957486136783734F, -0.2935304990757856F), Vector(-0.07245841035120149F, -0.12643253234750462F), Vector(-0.07245841035120149F, -0.01700554528650647F), Vector(-0.06802218114602589F, 0.042144177449168214F), Vector(-0.05914972273567468F, 0.09981515711645103F), Vector(-0.04584103512014788F, 0.15452865064695012F), Vector(-0.02957486136783734F, 0.21072088724584107F), Vector(-0.026617375231053605F, 0.21663585951940853F), Vector(-0.01922365988909427F, 0.21959334565619226F), Vector(-0.008872458410351202F, 0.22107208872458411F), Vector(0.008872458410351202F, 0.22107208872458411F), Vector(0.01922365988909427F, 0.21959334565619226F), Vector(0.026617375231053605F, 0.21663585951940853F), Vector(0.02957486136783734F, 0.21072088724584107F), Vector(0.04584103512014788F, 0.15452865064695012F), Vector(0.05914972273567468F, 0.09981515711645103F), Vector(0.06802218114602589F, 0.042144177449168214F), Vector(0.07245841035120149F, -0.01700554528650647F), Vector(0.07245841035120149F, -0.12643253234750462F)), listOf(Design.Trace(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0), 2.0F, ship_color), + Design.Trace(listOf(30, 31, 32, 33, 34, 35), 2.0F, ship_color), + Design.Trace(listOf(36, 37, 38, 39, 40, 41, 36), 2.0F, ship_color), + Design.Trace(listOf(42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 42), 2.0F, ship_color), + Design.Trace(listOf(56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73), 2.0F, ship_color))) + diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/util/font.kt b/app/src/main/java/net/dalsgaard/wearsignalk/util/font.kt new file mode 100644 index 0000000..9b85abe --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/util/font.kt @@ -0,0 +1,26 @@ +package net.dalsgaard.wearsignalk.util + +import androidx.compose.ui.graphics.Color + +data class Glyph (val points: List>, val left: Float, val right: Float) + +fun glyphJoin(glyphs: List, stroke: Float, color: Color) : Design +{ + if (glyphs.size == 0) { return Design(emptyList(), emptyList()) } + + // rearrange.... + var left_extend=0F + val d_points = mutableListOf() + val d_indices = mutableListOf>() + glyphs.forEach { + val move = Vector(left_extend - it.left, 0F) + it.points.forEach { + d_indices.add((d_points.size..(d_points.size + it.size)).toList()) + d_points.addAll (move + it) + } + left_extend = left_extend + (it.right - it.left) + } + val move_back = Vector(-left_extend/2, 0F) + return Design(move_back + d_points, d_indices.map { Design.Trace(it, stroke, color)}) +} + diff --git a/app/src/main/java/net/dalsgaard/wearsignalk/util/matrix.kt b/app/src/main/java/net/dalsgaard/wearsignalk/util/matrix.kt new file mode 100644 index 0000000..3da0ae8 --- /dev/null +++ b/app/src/main/java/net/dalsgaard/wearsignalk/util/matrix.kt @@ -0,0 +1,126 @@ +package net.dalsgaard.wearsignalk.util + +import androidx.compose.ui.graphics.Color +import kotlin.math.cos +import kotlin.math.max +import kotlin.math.min +import kotlin.math.sin + +/** + * Model for a 'Design' - which is a list of points in the 2D space + * along information about which points are to be connected with lines + * of certain width and color. + */ +class Design (val points: List, val traces: List) { + data class Trace(val indices: List, val width: Float, val color: Color) + data class BoundingBox(val x0: Float, val y0: Float, val x1: Float, val y1: Float) + + operator fun plus (o: Design) : Design { + return Design (points + o.points, traces + o.traces.map { + Trace(it.indices.map { it + points.size }, it.width, it.color) + }) + } + + operator fun plus (o: Vector) : Design { + return Design (o + points, traces) + } + + fun boundingBox () : BoundingBox { + return points.fold (BoundingBox(Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE)) { acc, nxt -> + BoundingBox( + min(nxt.x, acc.x0), + min(nxt.y, acc.y0), + max(nxt.x, acc.x1), + max(nxt.y, acc.y1) + ) + } + } +} + +/** + * Maps a 1 by 1 design centered on (0,0) onto a + * square screen with inverted Y. + */ +fun forSquareScreen (d: Design, edge: Float) : Design { + return scaleMatrix(edge) * (Matrix(1f, 0f, 0f, -1f)*d + Vector(0.5f, 0.5f)) +} + +/** + * Model for a 2D vector, along with basic operators + * + */ +data class Vector (val x: Float, val y: Float) { + + operator fun plus (o: Vector) : Vector { + return Vector (x+o.x, y+o.y) + } + + operator fun minus (o: Vector) : Vector { + return Vector (x-o.x, y-o.y) + } + + operator fun plus (o: List) : List { + return o.map { + Vector(x+it.x, y+it.y) + } + } + +} + +/** + * Model for a 2x2 matrix, along with a fairly extensive + * set of operators not only for matrices and vectors, but also + * for list of vectors and 'Designs'. + */ +data class Matrix (val a: Float, val b: Float, val c: Float, val d: Float) { + + operator fun plus (o: Matrix) : Matrix { + return Matrix(a+o.a, b+o.b, c+o.c, d+o.d) + } + + operator fun minus (o: Matrix) : Matrix { + return Matrix(a-o.a, b-o.b, c-o.c, d-o.d) + } + + operator fun times (o: Matrix) : Matrix { + return Matrix(a*o.a + b*o.c, a*o.b + b*o.d, c*o.a + d*o.c, c*o.b + d*o.d) + } + + operator fun times (o: Float) : Matrix { + return Matrix(a*o, b*o, c*o, d*o) + } + + operator fun times (o: List) : List { + return o.map { + Vector(a*it.x + b*it.y, c*it.x + d*it.y) + } + } + + operator fun times (o: Design) : Design { + return Design(this * o.points, o.traces) + } +} + +/** + * Returns 2x2 matrix for rotation in the plane, angle given in radians. + */ +fun rotMatrix(theta: Float) : Matrix { + return Matrix(cos(theta), -sin(theta), sin(theta), cos(theta)) +} + +/** + * Returns the identity matrix. + */ +fun eyeMatrix() : Matrix { + return Matrix(1f, 0f, 0f, 1f) +} + + +/** + * Returns a scaling matrix, + * basically identical to: eyeMatrix()*factor. + */ +fun scaleMatrix(factor: Float) : Matrix { + return Matrix(factor, 0f, 0f, factor) +} + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/values-round/strings.xml b/app/src/main/res/values-round/strings.xml new file mode 100644 index 0000000..42f1229 --- /dev/null +++ b/app/src/main/res/values-round/strings.xml @@ -0,0 +1,3 @@ + + From the Round world,\nHello, %1$s! + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..d016792 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + WearSignalK + + From the Square world,\nHello, %1$s! + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..b4cf450 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.1.2" apply false + id("org.jetbrains.kotlin.android") version "1.8.10" apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8ea0209 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,24 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..83863e3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Oct 07 23:54:01 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..c16357b --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "WearSignalK" +include(":app") + \ No newline at end of file