Rename project

This commit is contained in:
Tim Kluge 2024-12-08 11:54:18 +01:00
parent 9d57bfde6f
commit 0830674d62
27 changed files with 148 additions and 150 deletions

View File

@ -1,9 +1,10 @@
# Karoo Winddir Extension # Karoo Headwind Extension
> [!WARNING] > [!WARNING]
> This app is currently in prototype stage and its main features might not work at all. If you want to test it anyway and encounter issues, please report them in the [issue tracker](https://github.com/timklge/karoo-winddir/issues), ideally with adb logs attached. > This app is currently in prototype stage and its main features might not work at all. If you want to test it anyway and encounter issues, please report them in the [issue tracker](https://github.com/timklge/karoo-headwind/issues), ideally with adb logs attached.
This extension for Karoo devices adds a graphical data field that shows the current wind direction relative to the rider as an arrow. It also provides data fields for relative humidity, cloud coverage, wind speed, wind gust speed, surface pressure, and rainfall at the current location. This extension for Karoo devices adds a graphical data field that shows the current headwind direction as an arrow.
It also provides data fields for relative humidity, cloud coverage, wind speed, wind gust speed, surface pressure, and rainfall at the current location.
Compatible with Karoo 2 and Karoo 3 devices running Karoo OS version 1.524.2003 and later. Compatible with Karoo 2 and Karoo 3 devices running Karoo OS version 1.524.2003 and later.
@ -16,13 +17,13 @@ Compatible with Karoo 2 and Karoo 3 devices running Karoo OS version 1.524.2003
Currently, Hammerhead has not yet released an on-device app store for easy installation of extensions like this. Until then, you can sideload the app. Currently, Hammerhead has not yet released an on-device app store for easy installation of extensions like this. Until then, you can sideload the app.
1. Download the APK from the [releases page](https://github.com/timklge/karoo-winddir/releases) (or build it from source). 1. Download the APK from the [releases page](https://github.com/timklge/karoo-headwind/releases) (or build it from source).
2. Prepare your Karoo for sideloading by following the [step-by-step guide](https://www.dcrainmaker.com/2021/02/how-to-sideload-android-apps-on-your-hammerhead-karoo-1-karoo-2.html) by DC Rainmaker. 2. Prepare your Karoo for sideloading by following the [step-by-step guide](https://www.dcrainmaker.com/2021/02/how-to-sideload-android-apps-on-your-hammerhead-karoo-1-karoo-2.html) by DC Rainmaker.
3. Install the app using the command `adb install app-release.apk`. 3. Install the app using the command `adb install app-release.apk`.
## Usage ## Usage
After installing this app on your Karoo, you can add a data field showing the relative wind direction or one of the auxiliary fields to your data pages. The relative wind direction will be shown as an arrow image, with an optional overlay of the wind speed in your chosen unit of measurement (default is kilometers per hour). After installing this app on your Karoo, you can add a data field showing the headwind direction or one of the auxiliary fields to your data pages. The headwind direction will be shown as an arrow image, with an optional overlay of the wind speed in your chosen unit of measurement (default is kilometers per hour).
The app will automatically attempt to download weather data for your current approximate location from the [open-meteo.com](https://open-meteo.com) API once your device has acquired a GPS fix. The API service is free for non-commercial use. Your location is rounded to approximately two kilometers to maintain privacy. The data is updated when you ride more than two kilometers from the location where the weather data was downloaded or after one hour at the latest. If the app cannot connect to the weather service, it will retry the download every minute. Downloading weather data should work on Karoo 2 if you have a SIM card inserted or on Karoo 3 via your phone's internet connection if you have the Karoo companion app installed. The app will automatically attempt to download weather data for your current approximate location from the [open-meteo.com](https://open-meteo.com) API once your device has acquired a GPS fix. The API service is free for non-commercial use. Your location is rounded to approximately two kilometers to maintain privacy. The data is updated when you ride more than two kilometers from the location where the weather data was downloaded or after one hour at the latest. If the app cannot connect to the weather service, it will retry the download every minute. Downloading weather data should work on Karoo 2 if you have a SIM card inserted or on Karoo 3 via your phone's internet connection if you have the Karoo companion app installed.

View File

@ -6,15 +6,15 @@ plugins {
} }
android { android {
namespace = "de.timklge.karoowinddir" namespace = "de.timklge.karooheadwind"
compileSdk = 34 compileSdk = 34
defaultConfig { defaultConfig {
applicationId = "de.timklge.karoowinddir" applicationId = "de.timklge.karooheadwind"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 2
versionName = "1.0.0-beta1" versionName = "1.0.0-beta2"
} }
buildTypes { buildTypes {

View File

@ -1,11 +1,11 @@
{ {
"label": "karoo-winddir", "label": "karoo-headwind",
"packageName": "de.timklge.karoowinddir", "packageName": "de.timklge.karooheadwind",
"iconUrl": "https://github.com/timklge/karoo-winddir/releases/latest/download/karoo-winddir.png", "iconUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/karoo-headwind.png",
"latestApkUrl": "https://github.com/timklge/karoo-winddir/releases/latest/download/app-release.apk", "latestApkUrl": "https://github.com/timklge/karoo-headwind/releases/latest/download/app-release.apk",
"latestVersion": "1.0.0-beta1", "latestVersion": "1.0.0-beta2",
"latestVersionCode": 1, "latestVersionCode": 2,
"developer": "timklge", "developer": "timklge",
"description": "Provides relative wind direction, wind speed and other weather data fields", "description": "Provides headwind direction, wind speed and other weather data fields",
"releaseNotes": "Initial release" "releaseNotes": "Initial release"
} }

View File

@ -10,7 +10,7 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.AppCompat"> android:theme="@style/Theme.AppCompat">
<activity <activity
android:name="de.timklge.karoowinddir.MainActivity" android:name="de.timklge.karooheadwind.MainActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -19,7 +19,7 @@
</activity> </activity>
<service <service
android:name="de.timklge.karoowinddir.KarooWinddirExtension" android:name="de.timklge.karooheadwind.KarooHeadwindExtension"
android:exported="true" android:exported="true"
tools:ignore="ExportedService"> tools:ignore="ExportedService">
<intent-filter> <intent-filter>
@ -32,6 +32,6 @@
<meta-data <meta-data
android:name="io.hammerhead.karooext.MANIFEST_URL" android:name="io.hammerhead.karooext.MANIFEST_URL"
android:value="https://github.com/timklge/karoo-winddir/releases/latest/download/manifest.json" /> android:value="https://github.com/timklge/karoo-headwind/releases/latest/download/manifest.json" />
</application> </application>
</manifest> </manifest>

View File

@ -1,12 +1,12 @@
package de.timklge.karoowinddir package de.timklge.karooheadwind
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import de.timklge.karoowinddir.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import de.timklge.karoowinddir.screens.WinddirSettings import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karoowinddir.screens.WinddirStats import de.timklge.karooheadwind.screens.HeadwindStats
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.HttpResponseState import io.hammerhead.karooext.models.HttpResponseState
@ -42,13 +42,13 @@ val settingsKey = stringPreferencesKey("settings")
val currentDataKey = stringPreferencesKey("current") val currentDataKey = stringPreferencesKey("current")
val statsKey = stringPreferencesKey("stats") val statsKey = stringPreferencesKey("stats")
suspend fun saveSettings(context: Context, settings: WinddirSettings) { suspend fun saveSettings(context: Context, settings: HeadwindSettings) {
context.dataStore.edit { t -> context.dataStore.edit { t ->
t[settingsKey] = Json.encodeToString(settings) t[settingsKey] = Json.encodeToString(settings)
} }
} }
suspend fun saveStats(context: Context, stats: WinddirStats) { suspend fun saveStats(context: Context, stats: HeadwindStats) {
context.dataStore.edit { t -> context.dataStore.edit { t ->
t[statsKey] = Json.encodeToString(stats) t[statsKey] = Json.encodeToString(stats)
} }
@ -77,45 +77,45 @@ fun Context.streamCurrentWeatherData(): Flow<OpenMeteoCurrentWeatherResponse> {
val data = settingsJson[currentDataKey] val data = settingsJson[currentDataKey]
data?.let { d -> jsonWithUnknownKeys.decodeFromString<OpenMeteoCurrentWeatherResponse>(d) } data?.let { d -> jsonWithUnknownKeys.decodeFromString<OpenMeteoCurrentWeatherResponse>(d) }
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e(KarooWinddirExtension.TAG, "Failed to read preferences", e) Log.e(KarooHeadwindExtension.TAG, "Failed to read preferences", e)
null null
} }
}.filterNotNull().distinctUntilChanged().filter { it.current.time * 1000 >= System.currentTimeMillis() - (1000 * 60 * 60 * 12) } }.filterNotNull().distinctUntilChanged().filter { it.current.time * 1000 >= System.currentTimeMillis() - (1000 * 60 * 60 * 12) }
} }
fun Context.streamSettings(): Flow<WinddirSettings> { fun Context.streamSettings(): Flow<HeadwindSettings> {
return dataStore.data.map { settingsJson -> return dataStore.data.map { settingsJson ->
try { try {
jsonWithUnknownKeys.decodeFromString<WinddirSettings>( jsonWithUnknownKeys.decodeFromString<HeadwindSettings>(
settingsJson[settingsKey] ?: WinddirSettings.defaultSettings settingsJson[settingsKey] ?: HeadwindSettings.defaultSettings
) )
} catch(e: Throwable){ } catch(e: Throwable){
Log.e(KarooWinddirExtension.TAG, "Failed to read preferences", e) Log.e(KarooHeadwindExtension.TAG, "Failed to read preferences", e)
jsonWithUnknownKeys.decodeFromString<WinddirSettings>(WinddirSettings.defaultSettings) jsonWithUnknownKeys.decodeFromString<HeadwindSettings>(HeadwindSettings.defaultSettings)
} }
}.distinctUntilChanged() }.distinctUntilChanged()
} }
fun Context.streamStats(): Flow<WinddirStats> { fun Context.streamStats(): Flow<HeadwindStats> {
return dataStore.data.map { statsJson -> return dataStore.data.map { statsJson ->
try { try {
jsonWithUnknownKeys.decodeFromString<WinddirStats>( jsonWithUnknownKeys.decodeFromString<HeadwindStats>(
statsJson[statsKey] ?: WinddirStats.defaultStats statsJson[statsKey] ?: HeadwindStats.defaultStats
) )
} catch(e: Throwable){ } catch(e: Throwable){
Log.e(KarooWinddirExtension.TAG, "Failed to read stats", e) Log.e(KarooHeadwindExtension.TAG, "Failed to read stats", e)
jsonWithUnknownKeys.decodeFromString<WinddirStats>(WinddirStats.defaultStats) jsonWithUnknownKeys.decodeFromString<HeadwindStats>(HeadwindStats.defaultStats)
} }
}.distinctUntilChanged() }.distinctUntilChanged()
} }
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoordinates, settings: WinddirSettings): HttpResponseState.Complete { suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoordinates, settings: HeadwindSettings): HttpResponseState.Complete {
return callbackFlow { return callbackFlow {
// https://open-meteo.com/en/docs#current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=&daily=&location_mode=csv_coordinates&timeformat=unixtime&forecast_days=3 // https://open-meteo.com/en/docs#current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=&daily=&location_mode=csv_coordinates&timeformat=unixtime&forecast_days=3
val url = "https://api.open-meteo.com/v1/forecast?latitude=${gpsCoordinates.lat}&longitude=${gpsCoordinates.lon}&current=weather_code,temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m,surface_pressure&timeformat=unixtime&wind_speed_unit=${settings.windUnit.id}&precipitation_unit=${settings.precipitationUnit.id}" val url = "https://api.open-meteo.com/v1/forecast?latitude=${gpsCoordinates.lat}&longitude=${gpsCoordinates.lon}&current=weather_code,temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m,surface_pressure&timeformat=unixtime&wind_speed_unit=${settings.windUnit.id}&precipitation_unit=${settings.precipitationUnit.id}"
Log.d(KarooWinddirExtension.TAG, "Http request to ${url}...") Log.d(KarooHeadwindExtension.TAG, "Http request to ${url}...")
val listenerId = addConsumer( val listenerId = addConsumer(
OnHttpResponse.MakeHttpRequest( OnHttpResponse.MakeHttpRequest(
@ -124,7 +124,7 @@ suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoord
waitForConnection = false, waitForConnection = false,
), ),
) { event: OnHttpResponse -> ) { event: OnHttpResponse ->
Log.d(KarooWinddirExtension.TAG, "Http response event $event") Log.d(KarooHeadwindExtension.TAG, "Http response event $event")
if (event.state is HttpResponseState.Complete){ if (event.state is HttpResponseState.Complete){
trySend(event.state as HttpResponseState.Complete) trySend(event.state as HttpResponseState.Complete)
close() close()
@ -162,10 +162,10 @@ fun KarooSystemService.getGpsCoordinateFlow(): Flow<GpsCoordinates> {
val lon = values[DataType.Field.LOC_LONGITUDE] val lon = values[DataType.Field.LOC_LONGITUDE]
if (lat != null && lon != null){ if (lat != null && lon != null){
Log.d(KarooWinddirExtension.TAG, "Updated gps coords: $lat $lon") Log.d(KarooHeadwindExtension.TAG, "Updated gps coords: $lat $lon")
GpsCoordinates(lat, lon) GpsCoordinates(lat, lon)
} else { } else {
Log.e(KarooWinddirExtension.TAG, "Missing gps values: $values") Log.e(KarooHeadwindExtension.TAG, "Missing gps values: $values")
null null
} }
} }

View File

@ -1,17 +1,17 @@
package de.timklge.karoowinddir package de.timklge.karooheadwind
import android.util.Log import android.util.Log
import de.timklge.karoowinddir.datatypes.CloudCoverDataType import de.timklge.karooheadwind.datatypes.CloudCoverDataType
import de.timklge.karoowinddir.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import de.timklge.karoowinddir.datatypes.PrecipitationDataType import de.timklge.karooheadwind.datatypes.PrecipitationDataType
import de.timklge.karoowinddir.datatypes.RelativeHumidityDataType import de.timklge.karooheadwind.datatypes.RelativeHumidityDataType
import de.timklge.karoowinddir.datatypes.SurfacePressureDataType import de.timklge.karooheadwind.datatypes.SurfacePressureDataType
import de.timklge.karoowinddir.datatypes.WindDirectionDataType import de.timklge.karooheadwind.datatypes.WindDirectionDataType
import de.timklge.karoowinddir.datatypes.WindGustsDataType import de.timklge.karooheadwind.datatypes.WindGustsDataType
import de.timklge.karoowinddir.datatypes.WindSpeedDataType import de.timklge.karooheadwind.datatypes.WindSpeedDataType
import de.timklge.karoowinddir.datatypes.RelativeWindDirectionDataType import de.timklge.karooheadwind.datatypes.RelativeWindDirectionDataType
import de.timklge.karoowinddir.datatypes.WeatherDataType import de.timklge.karooheadwind.datatypes.WeatherDataType
import de.timklge.karoowinddir.screens.WinddirStats import de.timklge.karooheadwind.screens.HeadwindStats
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.KarooExtension import io.hammerhead.karooext.extension.KarooExtension
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -29,9 +29,9 @@ import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
class KarooWinddirExtension : KarooExtension("karoo-winddir", "1.0.0-beta1") { class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.0.0-beta2") {
companion object { companion object {
const val TAG = "karoo-winddir" const val TAG = "karoo-headwind"
} }
lateinit var karooSystem: KarooSystemService lateinit var karooSystem: KarooSystemService
@ -84,14 +84,14 @@ class KarooWinddirExtension : KarooExtension("karoo-winddir", "1.0.0-beta1") {
streamStats().first() streamStats().first()
} catch(e: Exception){ } catch(e: Exception){
Log.e(TAG, "Failed to read stats", e) Log.e(TAG, "Failed to read stats", e)
WinddirStats() HeadwindStats()
} }
val response = karooSystem.makeOpenMeteoHttpRequest(gps, settings) val response = karooSystem.makeOpenMeteoHttpRequest(gps, settings)
if (response.error != null){ if (response.error != null){
try { try {
val stats = lastKnownStats.copy(failedWeatherRequest = System.currentTimeMillis()) val stats = lastKnownStats.copy(failedWeatherRequest = System.currentTimeMillis())
launch { saveStats(this@KarooWinddirExtension, stats) } launch { saveStats(this@KarooHeadwindExtension, stats) }
} catch(e: Exception){ } catch(e: Exception){
Log.e(TAG, "Failed to write stats", e) Log.e(TAG, "Failed to write stats", e)
} }
@ -102,7 +102,7 @@ class KarooWinddirExtension : KarooExtension("karoo-winddir", "1.0.0-beta1") {
lastSuccessfulWeatherRequest = System.currentTimeMillis(), lastSuccessfulWeatherRequest = System.currentTimeMillis(),
lastSuccessfulWeatherPosition = gps lastSuccessfulWeatherPosition = gps
) )
launch { saveStats(this@KarooWinddirExtension, stats) } launch { saveStats(this@KarooHeadwindExtension, stats) }
} catch(e: Exception){ } catch(e: Exception){
Log.e(TAG, "Failed to write stats", e) Log.e(TAG, "Failed to write stats", e)
} }

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir package de.timklge.karooheadwind
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
@ -8,8 +8,8 @@ import androidx.compose.material3.Text
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import de.timklge.karoowinddir.screens.MainScreen import de.timklge.karooheadwind.screens.MainScreen
import de.timklge.karoowinddir.theme.AppTheme import de.timklge.karooheadwind.theme.AppTheme
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir package de.timklge.karooheadwind
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,10 +1,10 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import de.timklge.karoowinddir.KarooWinddirExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karoowinddir.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter import io.hammerhead.karooext.internal.Emitter
import io.hammerhead.karooext.models.DataPoint import io.hammerhead.karooext.models.DataPoint
@ -17,22 +17,22 @@ import kotlinx.coroutines.launch
abstract class BaseDataType( abstract class BaseDataType(
private val applicationContext: Context, private val applicationContext: Context,
dataTypeId: String dataTypeId: String
) : DataTypeImpl("karoo-winddir", dataTypeId) { ) : DataTypeImpl("karoo-headwind", dataTypeId) {
abstract fun getValue(data: OpenMeteoCurrentWeatherResponse): Double abstract fun getValue(data: OpenMeteoCurrentWeatherResponse): Double
override fun startStream(emitter: Emitter<StreamState>) { override fun startStream(emitter: Emitter<StreamState>) {
Log.d(KarooWinddirExtension.TAG, "start $dataTypeId stream") Log.d(KarooHeadwindExtension.TAG, "start $dataTypeId stream")
val job = CoroutineScope(Dispatchers.IO).launch { val job = CoroutineScope(Dispatchers.IO).launch {
val currentWeatherData = applicationContext.streamCurrentWeatherData() val currentWeatherData = applicationContext.streamCurrentWeatherData()
currentWeatherData.collect { data -> currentWeatherData.collect { data ->
val value = getValue(data) val value = getValue(data)
Log.d(KarooWinddirExtension.TAG, "$dataTypeId: $value") Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value")
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value)))) emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to value))))
} }
} }
emitter.setCancellable { emitter.setCancellable {
Log.d(KarooWinddirExtension.TAG, "stop $dataTypeId stream") Log.d(KarooHeadwindExtension.TAG, "stop $dataTypeId stream")
job.cancel() job.cancel()
} }
} }

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class CloudCoverDataType(context: Context) : BaseDataType(context, "cloudCover"){ class CloudCoverDataType(context: Context) : BaseDataType(context, "cloudCover"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.math.atan2 import kotlin.math.atan2

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class PrecipitationDataType(context: Context) : BaseDataType(context, "precipitation"){ class PrecipitationDataType(context: Context) : BaseDataType(context, "precipitation"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class RelativeHumidityDataType(context: Context) : BaseDataType(context, "relativeHumidity"){ class RelativeHumidityDataType(context: Context) : BaseDataType(context, "relativeHumidity"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,17 +1,17 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi
import androidx.glance.appwidget.GlanceRemoteViews import androidx.glance.appwidget.GlanceRemoteViews
import de.timklge.karoowinddir.KarooWinddirExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karoowinddir.getHeadingFlow import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karoowinddir.screens.WinddirSettings import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karoowinddir.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karoowinddir.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karoowinddir.streamSettings import de.timklge.karooheadwind.streamSettings
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter import io.hammerhead.karooext.internal.Emitter
@ -37,7 +37,7 @@ import kotlin.math.roundToInt
class RelativeWindDirectionDataType( class RelativeWindDirectionDataType(
private val karooSystem: KarooSystemService, private val karooSystem: KarooSystemService,
private val applicationContext: Context private val applicationContext: Context
) : DataTypeImpl("karoo-winddir", "winddir") { ) : DataTypeImpl("karoo-headwind", "headwind") {
private val glance = GlanceRemoteViews() private val glance = GlanceRemoteViews()
private fun signedAngleDifference(angle1: Double, angle2: Double): Double { private fun signedAngleDifference(angle1: Double, angle2: Double): Double {
@ -71,7 +71,7 @@ class RelativeWindDirectionDataType(
val windBearing = data.current.windDirection + 180 val windBearing = data.current.windDirection + 180
val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360 val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360
Log.d(KarooWinddirExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff") Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to diff)))) emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to diff))))
} }
} }
@ -81,13 +81,13 @@ class RelativeWindDirectionDataType(
} }
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) { override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
Log.d(KarooWinddirExtension.TAG, "Starting relative wind direction view with $emitter") Log.d(KarooHeadwindExtension.TAG, "Starting headwind direction view with $emitter")
val configJob = CoroutineScope(Dispatchers.IO).launch { val configJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(UpdateGraphicConfig(showHeader = false)) emitter.onNext(UpdateGraphicConfig(showHeader = false))
awaitCancellation() awaitCancellation()
} }
data class StreamData(val value: Double, val data: OpenMeteoCurrentWeatherResponse, val settings: WinddirSettings) data class StreamData(val value: Double, val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings)
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
karooSystem.streamDataFlow(dataTypeId) karooSystem.streamDataFlow(dataTypeId)
@ -100,7 +100,7 @@ class RelativeWindDirectionDataType(
emitter.updateView(result.remoteViews) emitter.updateView(result.remoteViews)
} }
.collect { streamData -> .collect { streamData ->
Log.d(KarooWinddirExtension.TAG, "Updating relative wind direction view") Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val windSpeed = streamData.data.current.windSpeed val windSpeed = streamData.data.current.windSpeed
val windDirection = streamData.value val windDirection = streamData.value
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
@ -114,7 +114,7 @@ class RelativeWindDirectionDataType(
} }
} }
emitter.setCancellable { emitter.setCancellable {
Log.d(KarooWinddirExtension.TAG, "Stopping winddir view with $emitter") Log.d(KarooHeadwindExtension.TAG, "Stopping headwind view with $emitter")
configJob.cancel() configJob.cancel()
viewJob.cancel() viewJob.cancel()
} }

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -20,7 +20,7 @@ import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview import androidx.glance.preview.Preview
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import de.timklge.karoowinddir.R import de.timklge.karooheadwind.R
import kotlin.math.roundToInt import kotlin.math.roundToInt
fun getArrowResourceByBearing(bearing: Int): Int { fun getArrowResourceByBearing(bearing: Int): Int {

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class SurfacePressureDataType(context: Context) : BaseDataType(context, "surfacePressure"){ class SurfacePressureDataType(context: Context) : BaseDataType(context, "surfacePressure"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,17 +1,17 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi import androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi
import androidx.glance.appwidget.GlanceRemoteViews import androidx.glance.appwidget.GlanceRemoteViews
import de.timklge.karoowinddir.KarooWinddirExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karoowinddir.WeatherInterpretation import de.timklge.karooheadwind.WeatherInterpretation
import de.timklge.karoowinddir.getHeadingFlow import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karoowinddir.screens.WinddirSettings import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karoowinddir.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karoowinddir.streamSettings import de.timklge.karooheadwind.streamSettings
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter import io.hammerhead.karooext.internal.Emitter
@ -34,7 +34,7 @@ import kotlin.math.roundToInt
class WeatherDataType( class WeatherDataType(
private val karooSystem: KarooSystemService, private val karooSystem: KarooSystemService,
private val applicationContext: Context private val applicationContext: Context
) : DataTypeImpl("karoo-winddir", "weather") { ) : DataTypeImpl("karoo-headwind", "weather") {
private val glance = GlanceRemoteViews() private val glance = GlanceRemoteViews()
// FIXME: Remove. Currently, the data field will permanently show "no sensor" if no data stream is provided // FIXME: Remove. Currently, the data field will permanently show "no sensor" if no data stream is provided
@ -44,7 +44,7 @@ class WeatherDataType(
currentWeatherData currentWeatherData
.collect { data -> .collect { data ->
Log.d(KarooWinddirExtension.TAG, "Wind code: ${data.current.weatherCode}") Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data.current.weatherCode}")
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to data.current.weatherCode.toDouble())))) emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to data.current.weatherCode.toDouble()))))
} }
} }
@ -54,13 +54,13 @@ class WeatherDataType(
} }
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) { override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
Log.d(KarooWinddirExtension.TAG, "Starting weather view with $emitter") Log.d(KarooHeadwindExtension.TAG, "Starting weather view with $emitter")
val configJob = CoroutineScope(Dispatchers.IO).launch { val configJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(UpdateGraphicConfig(showHeader = false)) emitter.onNext(UpdateGraphicConfig(showHeader = false))
awaitCancellation() awaitCancellation()
} }
data class StreamData(val data: OpenMeteoCurrentWeatherResponse, val settings: WinddirSettings) data class StreamData(val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings)
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
context.streamCurrentWeatherData() context.streamCurrentWeatherData()
@ -71,7 +71,7 @@ class WeatherDataType(
emitter.updateView(result.remoteViews) emitter.updateView(result.remoteViews)
} }
.collect { (data, settings) -> .collect { (data, settings) ->
Log.d(KarooWinddirExtension.TAG, "Updating weather view") Log.d(KarooHeadwindExtension.TAG, "Updating weather view")
val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode) val interpretation = WeatherInterpretation.fromWeatherCode(data.current.weatherCode)
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
@ -82,7 +82,7 @@ class WeatherDataType(
} }
} }
emitter.setCancellable { emitter.setCancellable {
Log.d(KarooWinddirExtension.TAG, "Stopping winddir view with $emitter") Log.d(KarooHeadwindExtension.TAG, "Stopping headwind view with $emitter")
configJob.cancel() configJob.cancel()
viewJob.cancel() viewJob.cancel()
} }

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -15,18 +15,15 @@ import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.height import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.layout.width import androidx.glance.layout.width
import androidx.glance.preview.ExperimentalGlancePreviewApi import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview import androidx.glance.preview.Preview
import androidx.glance.text.FontFamily import androidx.glance.text.FontFamily
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import de.timklge.karoowinddir.R import de.timklge.karooheadwind.R
import de.timklge.karoowinddir.WeatherInterpretation import de.timklge.karooheadwind.WeatherInterpretation
import de.timklge.karoowinddir.screens.WinddirSettings
fun getWeatherIcon(interpretation: WeatherInterpretation): Int { fun getWeatherIcon(interpretation: WeatherInterpretation): Int {
return when (interpretation){ return when (interpretation){

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
@ -16,9 +16,9 @@ import androidx.glance.layout.fillMaxSize
import androidx.glance.text.FontFamily import androidx.glance.text.FontFamily
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import de.timklge.karoowinddir.KarooWinddirExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karoowinddir.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.internal.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
@ -66,7 +66,7 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
7 -> "NW" 7 -> "NW"
else -> "N/A" else -> "N/A"
} }
Log.d( KarooWinddirExtension.TAG,"Updating wind direction view") Log.d( KarooHeadwindExtension.TAG,"Updating wind direction view")
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
Box(modifier = GlanceModifier.fillMaxSize(), Box(modifier = GlanceModifier.fillMaxSize(),
contentAlignment = Alignment( contentAlignment = Alignment(

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class WindGustsDataType(context: Context) : BaseDataType(context, "windGusts"){ class WindGustsDataType(context: Context) : BaseDataType(context, "windGusts"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,7 +1,7 @@
package de.timklge.karoowinddir.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karoowinddir.OpenMeteoCurrentWeatherResponse import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
class WindSpeedDataType(context: Context) : BaseDataType(context, "windSpeed"){ class WindSpeedDataType(context: Context) : BaseDataType(context, "windSpeed"){
override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double { override fun getValue(data: OpenMeteoCurrentWeatherResponse): Double {

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.screens package de.timklge.karooheadwind.screens
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.screens package de.timklge.karooheadwind.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -35,11 +35,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.timklge.karoowinddir.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import de.timklge.karoowinddir.getGpsCoordinateFlow import de.timklge.karooheadwind.getGpsCoordinateFlow
import de.timklge.karoowinddir.saveSettings import de.timklge.karooheadwind.saveSettings
import de.timklge.karoowinddir.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karoowinddir.streamStats import de.timklge.karooheadwind.streamStats
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -64,25 +64,25 @@ enum class PrecipitationUnit(val id: String, val label: String){
} }
@Serializable @Serializable
data class WinddirSettings( data class HeadwindSettings(
val windUnit: WindUnit = WindUnit.KILOMETERS_PER_HOUR, val windUnit: WindUnit = WindUnit.KILOMETERS_PER_HOUR,
val precipitationUnit: PrecipitationUnit = PrecipitationUnit.MILLIMETERS, val precipitationUnit: PrecipitationUnit = PrecipitationUnit.MILLIMETERS,
val welcomeDialogAccepted: Boolean = false, val welcomeDialogAccepted: Boolean = false,
val showWindspeedOverlay: Boolean = true val showWindspeedOverlay: Boolean = true
){ ){
companion object { companion object {
val defaultSettings = Json.encodeToString(WinddirSettings()) val defaultSettings = Json.encodeToString(HeadwindSettings())
} }
} }
@Serializable @Serializable
data class WinddirStats( data class HeadwindStats(
val lastSuccessfulWeatherRequest: Long? = null, val lastSuccessfulWeatherRequest: Long? = null,
val lastSuccessfulWeatherPosition: GpsCoordinates? = null, val lastSuccessfulWeatherPosition: GpsCoordinates? = null,
val failedWeatherRequest: Long? = null, val failedWeatherRequest: Long? = null,
){ ){
companion object { companion object {
val defaultStats = Json.encodeToString(WinddirStats()) val defaultStats = Json.encodeToString(HeadwindStats())
} }
} }
@ -99,7 +99,7 @@ fun MainScreen() {
var welcomeDialogVisible by remember { mutableStateOf(false) } var welcomeDialogVisible by remember { mutableStateOf(false) }
var showWindspeedOverlay by remember { mutableStateOf(false) } var showWindspeedOverlay by remember { mutableStateOf(false) }
val stats by ctx.streamStats().collectAsState(WinddirStats()) val stats by ctx.streamStats().collectAsState(HeadwindStats())
val location by karooSystem.getGpsCoordinateFlow().collectAsState(initial = null) val location by karooSystem.getGpsCoordinateFlow().collectAsState(initial = null)
var savedDialogVisible by remember { mutableStateOf(false) } var savedDialogVisible by remember { mutableStateOf(false) }
@ -143,13 +143,13 @@ fun MainScreen() {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Switch(checked = showWindspeedOverlay, onCheckedChange = { showWindspeedOverlay = it}) Switch(checked = showWindspeedOverlay, onCheckedChange = { showWindspeedOverlay = it})
Spacer(modifier = Modifier.width(10.dp)) Spacer(modifier = Modifier.width(10.dp))
Text("Show wind speed on direction field") Text("Show headwind speed on arrow")
} }
FilledTonalButton(modifier = Modifier FilledTonalButton(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(50.dp), onClick = { .height(50.dp), onClick = {
val newSettings = WinddirSettings(windUnit = selectedWindUnit, precipitationUnit = selectedPrecipitationUnit, welcomeDialogAccepted = true, showWindspeedOverlay = showWindspeedOverlay) val newSettings = HeadwindSettings(windUnit = selectedWindUnit, precipitationUnit = selectedPrecipitationUnit, welcomeDialogAccepted = true, showWindspeedOverlay = showWindspeedOverlay)
coroutineScope.launch { coroutineScope.launch {
saveSettings(ctx, newSettings) saveSettings(ctx, newSettings)
@ -198,16 +198,16 @@ fun MainScreen() {
AlertDialog(onDismissRequest = { }, AlertDialog(onDismissRequest = { },
confirmButton = { Button(onClick = { confirmButton = { Button(onClick = {
coroutineScope.launch { coroutineScope.launch {
saveSettings(ctx, WinddirSettings(windUnit = selectedWindUnit, precipitationUnit = selectedPrecipitationUnit, welcomeDialogAccepted = true)) saveSettings(ctx, HeadwindSettings(windUnit = selectedWindUnit, precipitationUnit = selectedPrecipitationUnit, welcomeDialogAccepted = true))
} }
}) { Text("OK") } }, }) { Text("OK") } },
text = { text = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
Text("Welcome to karoo-winddir!") Text("Welcome to karoo-headwind!")
Spacer(Modifier.padding(10.dp)) Spacer(Modifier.padding(10.dp))
Text("You can add relative wind direction and other fields to your data pages in your profile settings.") Text("You can add headwind direction and other fields to your data pages in your profile settings.")
Spacer(Modifier.padding(10.dp)) Spacer(Modifier.padding(10.dp))

View File

@ -1,4 +1,4 @@
package de.timklge.karoowinddir.theme package de.timklge.karooheadwind.theme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View File

@ -1,8 +1,8 @@
<resources> <resources>
<string name="app_name">WindDir</string> <string name="app_name">WindDir</string>
<string name="extension_name">winddir</string> <string name="extension_name">headwind</string>
<string name="winddir">Wind direction</string> <string name="headwind">Wind direction</string>
<string name="winddir_description">Current wind direction relative to the riding direction</string> <string name="headwind_description">Current wind direction relative to the riding direction</string>
<string name="relativeHumidity">Humidity</string> <string name="relativeHumidity">Humidity</string>
<string name="relativeHumidity_description">Relative humidity in percent</string> <string name="relativeHumidity_description">Relative humidity in percent</string>
<string name="cloudCover">Cloud cover</string> <string name="cloudCover">Cloud cover</string>

View File

@ -2,14 +2,14 @@
<ExtensionInfo <ExtensionInfo
displayName="@string/extension_name" displayName="@string/extension_name"
icon="@drawable/ic_launcher" icon="@drawable/ic_launcher"
id="karoo-winddir" id="karoo-headwind"
scansDevices="false"> scansDevices="false">
<DataType <DataType
description="@string/winddir_description" description="@string/headwind_description"
displayName="@string/winddir" displayName="@string/headwind"
graphical="true" graphical="true"
icon="@drawable/ic_launcher" icon="@drawable/ic_launcher"
typeId="winddir" /> typeId="headwind" />
<DataType <DataType
description="@string/weather_description" description="@string/weather_description"

View File

@ -34,5 +34,5 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "Karoo Winddir" rootProject.name = "Karoo Headwind"
include("app") include("app")