Rename project
This commit is contained in:
parent
9d57bfde6f
commit
0830674d62
11
README.md
11
README.md
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
@ -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}¤t=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}¤t=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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
@ -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")
|
||||||
|
|
||||||
@ -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
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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 {
|
||||||
@ -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
|
||||||
@ -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 {
|
||||||
@ -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 {
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
@ -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 {
|
||||||
@ -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 {
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
@ -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){
|
||||||
@ -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(
|
||||||
@ -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 {
|
||||||
@ -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 {
|
||||||
@ -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
|
||||||
@ -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))
|
||||||
|
|
||||||
@ -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
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -34,5 +34,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "Karoo Winddir"
|
rootProject.name = "Karoo Headwind"
|
||||||
include("app")
|
include("app")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user