Compare commits

..

No commits in common. "522364dd848ba2770157b154da559c087fbcc3d6" and "c4a23ce4565336e0a55a43766b985207d5b82e50" have entirely different histories.

65 changed files with 348 additions and 1092 deletions

View File

@ -25,6 +25,7 @@ jobs:
echo "KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> $GITHUB_ENV echo "KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> $GITHUB_ENV
echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV
echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV
echo "GOOGLE_SERVICES_JSON_BASE64=${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}" >> $GITHUB_ENV
echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: set up JDK 17 - name: set up JDK 17

View File

@ -25,7 +25,7 @@ After installing this app on your Karoo and opening it once from the main menu,
- Tailwind with riding speed (graphical, 1x1 field): Shows an arrow indicating the current headwind direction next to a label reading your current speed and the speed of the tailwind. If you ride against a headwind of 5 mph, it will show "-5". If you ride in the same direction of a 5 mph wind, it will read "+5". Text and arrow are colored based on the tailwind speed, with red indicating a strong headwind and green indicating a strong tailwind. - Tailwind with riding speed (graphical, 1x1 field): Shows an arrow indicating the current headwind direction next to a label reading your current speed and the speed of the tailwind. If you ride against a headwind of 5 mph, it will show "-5". If you ride in the same direction of a 5 mph wind, it will read "+5". Text and arrow are colored based on the tailwind speed, with red indicating a strong headwind and green indicating a strong tailwind.
- Tailwind (graphical, 1x1 field): Similar to the tailwind and riding speed field, but shows tailwind speed, wind speed and wind gust speed instead of riding speed. - Tailwind (graphical, 1x1 field): Similar to the tailwind and riding speed field, but shows tailwind speed, wind speed and wind gust speed instead of riding speed.
- Weather forecast (graphical, 2x1 field): Shows three columns indicating the current weather conditions (sunny, cloudy, ...), wind direction, precipitation and temperature forecasted for the next three hours. Tap on this widget to cycle through the 12 hour forecast. If you have a route loaded, the forecast widget will show the forecasted weather along points of the route, with an estimated traveled distance per hour of 20 km / 12 miles by default. - Weather forecast (graphical, 2x1 field): Shows three columns indicating the current weather conditions (sunny, cloudy, ...), wind direction, precipitation and temperature forecasted for the next three hours. Tap on this widget to cycle through the 12 hour forecast. If you have a route loaded, the forecast widget will show the forecasted weather along points of the route, with an estimated traveled distance per hour of 20 km / 12 miles by default.
- Current weather (graphical, 1x1 field): Shows current weather conditions (same as forecast widget, but only for the current time). - Current weather (graphical, 1x1 field): Shows current weather conditions (same as forecast widget, but only for the current time). Tap on this widget to open the headwind app with a forecast overview.
- Relative grade (numerical): Shows the relative grade. The relative grade is calculated by estimating the force of the headwind, and then calculating the gradient you would need to ride at to experience this resistance if there was no wind. Example: If you are riding on an actual gradient of 2 %, face a headwind of 18 km/h while riding at 29 km/h, the relative grade will be shown as 5.2 % (with 3.2 % added to the actual grade due to the headwind). - Relative grade (numerical): Shows the relative grade. The relative grade is calculated by estimating the force of the headwind, and then calculating the gradient you would need to ride at to experience this resistance if there was no wind. Example: If you are riding on an actual gradient of 2 %, face a headwind of 18 km/h while riding at 29 km/h, the relative grade will be shown as 5.2 % (with 3.2 % added to the actual grade due to the headwind).
- Relative elevation gain (numerical): Shows the relative elegation gain. The relative elevation gain is calculated using the relative grade and is an estimation of how much climbing would have been equivalent to the headwind you faced during the ride. - Relative elevation gain (numerical): Shows the relative elegation gain. The relative elevation gain is calculated using the relative grade and is an estimation of how much climbing would have been equivalent to the headwind you faced during the ride.
- Additionally, data fields that only show the current data value for headwind speed, humidity, cloud cover, absolute wind speed, absolute wind gust speed, absolute wind direction, rainfall and surface pressure can be added if desired. - Additionally, data fields that only show the current data value for headwind speed, humidity, cloud cover, absolute wind speed, absolute wind gust speed, absolute wind direction, rainfall and surface pressure can be added if desired.
@ -41,7 +41,7 @@ If the app cannot connect to the weather service, it will retry the download eve
## Credits ## Credits
- Icons are from [boxicons.com](https://boxicons.com) ([MIT-licensed](icon_credits.txt)) and the [Google Noto Color Emoji font](https://fonts.google.com/noto/specimen/Noto+Color+Emoji) (SIL Open Font License 1.1) - Icons are from [boxicons.com](https://boxicons.com) ([MIT-licensed](icon_credits.txt))
- Made possible by the generous usage terms of [open-meteo.com](https://open-meteo.com) - Made possible by the generous usage terms of [open-meteo.com](https://open-meteo.com)
- Interfaces with [openweathermap.org](https://openweathermap.org) - Interfaces with [openweathermap.org](https://openweathermap.org)
- Uses [karoo-ext](https://github.com/hammerheadnav/karoo-ext) (Apache2-licensed) - Uses [karoo-ext](https://github.com/hammerheadnav/karoo-ext) (Apache2-licensed)

1
app/.gitignore vendored
View File

@ -1 +1,2 @@
/build /build
/google-services.json

View File

@ -5,6 +5,8 @@ plugins {
alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.compiler)
kotlin("plugin.serialization") version "2.0.20" kotlin("plugin.serialization") version "2.0.20"
alias(libs.plugins.google.gms.google.services)
alias(libs.plugins.google.firebase.crashlytics)
} }
android { android {
@ -35,6 +37,9 @@ android {
buildTypes { buildTypes {
debug { debug {
isMinifyEnabled = false isMinifyEnabled = false
firebaseCrashlytics {
mappingFileUploadEnabled = false
}
} }
release { release {
signingConfig = signingConfigs.getByName("release") signingConfig = signingConfigs.getByName("release")
@ -55,6 +60,24 @@ android {
} }
} }
tasks.register("addGoogleServicesJson") {
description = "Adds google-services.json to the project"
group = "build"
doLast {
val googleServicesJson = System.getenv("GOOGLE_SERVICES_JSON_BASE64")
?.let { Base64.getDecoder().decode(it) }
?.let { String(it) }
if (googleServicesJson != null) {
val jsonFile = file("$projectDir/google-services.json")
jsonFile.writeText(googleServicesJson)
println("Added google-services.json to the project")
} else {
println("No GOOGLE_SERVICES_JSON_BASE64 environment variable found, skipping...")
}
}
}
tasks.register("generateManifest") { tasks.register("generateManifest") {
description = "Generates manifest.json with current version information" description = "Generates manifest.json with current version information"
group = "build" group = "build"
@ -70,8 +93,11 @@ tasks.register("generateManifest") {
"latestVersionCode" to android.defaultConfig.versionCode, "latestVersionCode" to android.defaultConfig.versionCode,
"developer" to "github.com/timklge", "developer" to "github.com/timklge",
"description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.", "description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.",
"releaseNotes" to "* Remove crashlytics\n" + "releaseNotes" to "* Reduce refresh rate on K2, add refresh rate setting\n" +
"* Reduce refresh rate on K2, add refresh rate setting\n" + "* Fix weather data download from Open-Meteo via iOS companion app (thx @keefar!)\n" +
"* Remove custom wind speed unit setting and always use imperial / metric as set in profile\n" +
"* Add relative grade, relative elevation gain data fields\n" +
"* Add OpenWeatherMap support contributed by lockevod\n",
"screenshotUrls" to listOf( "screenshotUrls" to listOf(
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png", "https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png",
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview3.png", "https://github.com/timklge/karoo-headwind/releases/latest/download/preview3.png",
@ -88,6 +114,7 @@ tasks.register("generateManifest") {
tasks.named("assemble") { tasks.named("assemble") {
dependsOn("generateManifest") dependsOn("generateManifest")
dependsOn("addGoogleServicesJson")
} }
dependencies { dependencies {
@ -102,5 +129,6 @@ dependencies {
implementation(libs.androidx.glance.appwidget) implementation(libs.androidx.glance.appwidget)
implementation(libs.androidx.glance.appwidget.preview) implementation(libs.androidx.glance.appwidget.preview)
implementation(libs.androidx.glance.preview) implementation(libs.androidx.glance.preview)
implementation(libs.firebase.crashlytics)
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
} }

View File

@ -210,22 +210,6 @@ fun Context.streamCurrentForecastWeatherData(): Flow<WeatherDataResponse?> {
}.distinctUntilChanged() }.distinctUntilChanged()
} }
fun lerp(
start: Double,
end: Double,
factor: Double
): Double {
return start + (end - start) * factor
}
fun lerp(
start: Int,
end: Int,
factor: Double
): Int {
return (start + (end - start) * factor).toInt()
}
fun lerpNullable( fun lerpNullable(
start: Double?, start: Double?,
end: Double?, end: Double?,
@ -282,18 +266,17 @@ fun lerpWeather(
return WeatherData( return WeatherData(
time = (start.time + (end.time - start.time) * factor).toLong(), time = (start.time + (end.time - start.time) * factor).toLong(),
temperature = start.temperature + (end.temperature - start.temperature) * factor, temperature = start.temperature + (end.temperature - start.temperature) * factor,
relativeHumidity = lerp(start.relativeHumidity, end.relativeHumidity, factor), relativeHumidity = lerpNullable(start.relativeHumidity, end.relativeHumidity, factor),
precipitation = start.precipitation + (end.precipitation - start.precipitation) * factor, precipitation = start.precipitation + (end.precipitation - start.precipitation) * factor,
precipitationProbability = lerpNullable(start.precipitationProbability, end.precipitationProbability, factor), precipitationProbability = lerpNullable(start.precipitationProbability, end.precipitationProbability, factor),
cloudCover = lerp(start.cloudCover, end.cloudCover, factor), cloudCover = lerpNullable(start.cloudCover, end.cloudCover, factor),
surfacePressure = lerp(start.surfacePressure, end.surfacePressure, factor), surfacePressure = lerpNullable(start.surfacePressure, end.surfacePressure, factor),
sealevelPressure = lerp(start.sealevelPressure, end.sealevelPressure, factor), sealevelPressure = lerpNullable(start.sealevelPressure, end.sealevelPressure, factor),
windSpeed = start.windSpeed + (end.windSpeed - start.windSpeed) * factor, windSpeed = start.windSpeed + (end.windSpeed - start.windSpeed) * factor,
windDirection = lerpAngle(start.windDirection, end.windDirection, factor), windDirection = lerpAngle(start.windDirection, end.windDirection, factor),
windGusts = start.windGusts + (end.windGusts - start.windGusts) * factor, windGusts = start.windGusts + (end.windGusts - start.windGusts) * factor,
weatherCode = closestWeatherData.weatherCode, weatherCode = closestWeatherData.weatherCode,
isForecast = closestWeatherData.isForecast, isForecast = closestWeatherData.isForecast
isNight = closestWeatherData.isNight,
) )
} }

View File

@ -1,7 +1,6 @@
package de.timklge.karooheadwind package de.timklge.karooheadwind
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.ActiveRidePage
import io.hammerhead.karooext.models.OnLocationChanged import io.hammerhead.karooext.models.OnLocationChanged
import io.hammerhead.karooext.models.OnNavigationState import io.hammerhead.karooext.models.OnNavigationState
import io.hammerhead.karooext.models.OnStreamState import io.hammerhead.karooext.models.OnStreamState
@ -13,7 +12,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.transform
@ -67,22 +65,3 @@ fun<T> Flow<T>.throttle(timeout: Long): Flow<T> = this
emit(it) emit(it)
delay(timeout) delay(timeout)
} }
fun KarooSystemService.streamActiveRidePage(): Flow<ActiveRidePage> {
return callbackFlow {
val listenerId = addConsumer { activeRidePage: ActiveRidePage ->
trySendBlocking(activeRidePage)
}
awaitClose {
removeConsumer(listenerId)
}
}
}
fun KarooSystemService.streamDatatypeIsVisible(
datatype: String,
): Flow<Boolean> {
return streamActiveRidePage().map { page ->
page.page.elements.any { it.dataTypeId == datatype }
}
}

View File

@ -50,6 +50,7 @@ data class HeadwindWidgetSettings(
} }
} }
//Moded with openweahtermap.org
@Serializable @Serializable
data class HeadwindStats( data class HeadwindStats(
val lastSuccessfulWeatherRequest: Long? = null, val lastSuccessfulWeatherRequest: Long? = null,
@ -69,8 +70,8 @@ enum class RefreshRate(val id: String, val k2Ms: Long, val k3Ms: Long) {
SLOW("slow", 5_000L, 3_000L), SLOW("slow", 5_000L, 3_000L),
MINIMUM("minimum", 10_000L, 10_000L); MINIMUM("minimum", 10_000L, 10_000L);
fun getDescription(isOnK2: Boolean): String { fun getDescription(karooSystemService: KarooSystemService): String {
return if (isOnK2) { return if (karooSystemService.hardwareType == HardwareType.K2) {
when (this) { when (this) {
FAST -> "Fast (1s)" FAST -> "Fast (1s)"
STANDARD -> "Standard (2s)" STANDARD -> "Standard (2s)"

View File

@ -3,24 +3,16 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.streamCurrentWeatherData
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
import io.hammerhead.karooext.internal.ViewEmitter
import io.hammerhead.karooext.models.DataPoint import io.hammerhead.karooext.models.DataPoint
import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
import io.hammerhead.karooext.models.UpdateGraphicConfig
import io.hammerhead.karooext.models.UserProfile
import io.hammerhead.karooext.models.ViewConfig
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -29,25 +21,17 @@ abstract class BaseDataType(
private val applicationContext: Context, private val applicationContext: Context,
dataTypeId: String dataTypeId: String
) : DataTypeImpl("karoo-headwind", dataTypeId) { ) : DataTypeImpl("karoo-headwind", dataTypeId) {
abstract fun getValue(data: WeatherData, userProfile: UserProfile): Double? abstract fun getValue(data: WeatherData): Double?
open fun getFormatDataType(): String? = null
override fun startStream(emitter: Emitter<StreamState>) { override fun startStream(emitter: Emitter<StreamState>) {
Log.d(KarooHeadwindExtension.TAG, "start $dataTypeId stream") Log.d(KarooHeadwindExtension.TAG, "start $dataTypeId stream")
val job = CoroutineScope(Dispatchers.IO).launch { val job = CoroutineScope(Dispatchers.IO).launch {
data class StreamData(val weatherData: WeatherData, val userProfile: UserProfile) val currentWeatherData = applicationContext.streamCurrentWeatherData(karooSystemService)
val currentWeatherData = combine(applicationContext.streamCurrentWeatherData(karooSystemService).filterNotNull(), karooSystemService.streamUserProfile()) { weatherData, userProfile -> currentWeatherData
StreamData(weatherData, userProfile) .filterNotNull()
} .collect { data ->
val value = getValue(data)
val refreshRate = karooSystemService.getRefreshRateInMilliseconds(applicationContext)
currentWeatherData.filterNotNull()
.throttle(refreshRate)
.collect { (data, userProfile) ->
val value = getValue(data, userProfile)
Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value") Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value")
if (value != null) { if (value != null) {
@ -62,12 +46,4 @@ abstract class BaseDataType(
job.cancel() job.cancel()
} }
} }
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
Log.d(KarooHeadwindExtension.TAG, "Starting $dataTypeId view with $emitter")
if (getFormatDataType() != null){
emitter.onNext(UpdateGraphicConfig(formatDataTypeId = getFormatDataType()))
}
}
} }

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class CloudCoverDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "cloudCover"){ class CloudCoverDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "cloudCover"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? { override fun getValue(data: WeatherData): Double? {
return data.cloudCover return data.cloudCover
} }
} }

View File

@ -28,22 +28,18 @@ import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.R import de.timklge.karooheadwind.R
import de.timklge.karooheadwind.TemperatureUnit import de.timklge.karooheadwind.TemperatureUnit
import de.timklge.karooheadwind.UpcomingRoute import de.timklge.karooheadwind.UpcomingRoute
import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.weatherprovider.WeatherDataForLocation
import de.timklge.karooheadwind.WeatherDataProvider import de.timklge.karooheadwind.WeatherDataProvider
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
import de.timklge.karooheadwind.getHeadingFlow import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.streamCurrentForecastWeatherData import de.timklge.karooheadwind.streamCurrentForecastWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUpcomingRoute import de.timklge.karooheadwind.streamUpcomingRoute
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.streamWidgetSettings import de.timklge.karooheadwind.streamWidgetSettings
import de.timklge.karooheadwind.throttle import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.util.celciusInUserUnit
import de.timklge.karooheadwind.util.millimetersInUserUnit
import de.timklge.karooheadwind.util.msInUserUnit
import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.weatherprovider.WeatherDataForLocation
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
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.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
@ -58,7 +54,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -83,8 +78,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean)
isNight: Boolean)
@OptIn(ExperimentalGlanceRemoteViewsApi::class) @OptIn(ExperimentalGlanceRemoteViewsApi::class)
private val glance = GlanceRemoteViews() private val glance = GlanceRemoteViews()
@ -95,7 +89,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
data class StreamData(val data: WeatherDataResponse?, val settings: SettingsAndProfile, data class StreamData(val data: WeatherDataResponse?, val settings: SettingsAndProfile,
val widgetSettings: HeadwindWidgetSettings? = null, val widgetSettings: HeadwindWidgetSettings? = null,
val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null, val isVisible: Boolean) val headingResponse: HeadingResponse? = null, val upcomingRoute: UpcomingRoute? = null)
data class SettingsAndProfile(val settings: HeadwindSettings, val isImperial: Boolean, val isImperialTemperature: Boolean) data class SettingsAndProfile(val settings: HeadwindSettings, val isImperial: Boolean, val isImperialTemperature: Boolean)
@ -119,7 +113,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
WeatherData( WeatherData(
time = forecastTime, time = forecastTime,
temperature = forecastTemperature, temperature = forecastTemperature,
relativeHumidity = 20, relativeHumidity = 20.0,
precipitation = forecastPrecipitation, precipitation = forecastPrecipitation,
cloudCover = 3.0, cloudCover = 3.0,
sealevelPressure = 1013.25, sealevelPressure = 1013.25,
@ -129,8 +123,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
windDirection = forecastWindDirection, windDirection = forecastWindDirection,
windGusts = forecastWindGusts, windGusts = forecastWindGusts,
weatherCode = forecastWeatherCode, weatherCode = forecastWeatherCode,
isForecast = true, isForecast = true
isNight = it < 2
) )
} }
@ -142,7 +135,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
current = WeatherData( current = WeatherData(
time = timeAtFullHour, time = timeAtFullHour,
temperature = 20.0, temperature = 20.0,
relativeHumidity = 20, relativeHumidity = 20.0,
precipitation = 0.0, precipitation = 0.0,
cloudCover = 3.0, cloudCover = 3.0,
sealevelPressure = 1013.25, sealevelPressure = 1013.25,
@ -151,8 +144,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
windDirection = 180.0, windDirection = 180.0,
windGusts = 10.0, windGusts = 10.0,
weatherCode = WeatherInterpretation.getKnownWeatherCodes().random(), weatherCode = WeatherInterpretation.getKnownWeatherCodes().random(),
isForecast = false, isForecast = false
isNight = false
), ),
coords = GpsCoordinates(0.0, 0.0, distanceAlongRoute = index * distancePerHour), coords = GpsCoordinates(0.0, 0.0, distanceAlongRoute = index * distancePerHour),
timezone = "UTC", timezone = "UTC",
@ -169,8 +161,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
HeadwindSettings(), HeadwindSettings(),
settingsAndProfile?.isImperial == true, settingsAndProfile?.isImperial == true,
settingsAndProfile?.isImperialTemperature == true settingsAndProfile?.isImperialTemperature == true
), )
isVisible = true
) )
) )
@ -212,23 +203,14 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
if (oldDistance == null || newDistance == null) return@distinctUntilChanged false if (oldDistance == null || newDistance == null) return@distinctUntilChanged false
abs(oldDistance - newDistance) < 1_000 abs(oldDistance - newDistance) < 1_000
}, }
karooSystem.streamDatatypeIsVisible(dataTypeId) ) { weatherData, settings, widgetSettings, heading, upcomingRoute ->
) { data ->
val weatherData = data[0] as WeatherDataResponse?
val settings = data[1] as SettingsAndProfile
val widgetSettings = data[2] as HeadwindWidgetSettings?
val heading = data[3] as HeadingResponse?
val upcomingRoute = data[4] as UpcomingRoute?
val isVisible = data[5] as Boolean
StreamData( StreamData(
data = weatherData, data = weatherData,
settings = settings, settings = settings,
widgetSettings = widgetSettings, widgetSettings = widgetSettings,
headingResponse = heading, headingResponse = heading,
upcomingRoute = upcomingRoute, upcomingRoute = upcomingRoute
isVisible = isVisible
) )
} }
} }
@ -236,7 +218,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(ShowCustomStreamState("", null)) emitter.onNext(ShowCustomStreamState("", null))
dataFlow.filter { it.isVisible }.collect { (allData, settingsAndProfile, widgetSettings, headingResponse, upcomingRoute) -> dataFlow.collect { (allData, settingsAndProfile, widgetSettings, headingResponse, upcomingRoute) ->
Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view") Log.d(KarooHeadwindExtension.TAG, "Updating weather forecast view")
if (allData?.data.isNullOrEmpty()){ if (allData?.data.isNullOrEmpty()){
@ -314,17 +296,16 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
arrowBitmap = baseBitmap, arrowBitmap = baseBitmap,
current = interpretation, current = interpretation,
windBearing = data.current.windDirection.roundToInt(), windBearing = data.current.windDirection.roundToInt(),
windSpeed = msInUserUnit(data.current.windSpeed, settingsAndProfile.isImperial).roundToInt(), windSpeed = data.current.windSpeed.roundToInt(),
windGusts = msInUserUnit(data.current.windGusts, settingsAndProfile.isImperial).roundToInt(), windGusts = data.current.windGusts.roundToInt(),
precipitation = millimetersInUserUnit(data.current.precipitation, settingsAndProfile.isImperial), precipitation = data.current.precipitation,
precipitationProbability = null, precipitationProbability = null,
temperature = celciusInUserUnit(data.current.temperature, settingsAndProfile.isImperialTemperature).roundToInt(), temperature = data.current.temperature.roundToInt(),
temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS, temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS,
timeLabel = formattedTime, timeLabel = formattedTime,
dateLabel = if (hasNewDate) formattedDate else null, dateLabel = if (hasNewDate) formattedDate else null,
distance = null, distance = null,
isImperial = settingsAndProfile.isImperial, isImperial = settingsAndProfile.isImperial
isNight = data.current.isNight
) )
previousDate = formattedDate previousDate = formattedDate
@ -340,17 +321,16 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
arrowBitmap = baseBitmap, arrowBitmap = baseBitmap,
current = interpretation, current = interpretation,
windBearing = weatherData?.windDirection?.roundToInt() ?: 0, windBearing = weatherData?.windDirection?.roundToInt() ?: 0,
windSpeed = msInUserUnit(weatherData?.windSpeed ?: 0.0, settingsAndProfile.isImperial).roundToInt(), windSpeed = weatherData?.windSpeed?.roundToInt() ?: 0,
windGusts = msInUserUnit(weatherData?.windGusts ?: 0.0, settingsAndProfile.isImperial).roundToInt(), windGusts = weatherData?.windGusts?.roundToInt() ?: 0,
precipitation = millimetersInUserUnit(weatherData?.precipitation ?: 0.0, settingsAndProfile.isImperial), precipitation = weatherData?.precipitation ?: 0.0,
precipitationProbability = weatherData?.precipitationProbability?.toInt(), precipitationProbability = weatherData?.precipitationProbability?.toInt(),
temperature = celciusInUserUnit(weatherData?.temperature ?: 0.0, settingsAndProfile.isImperialTemperature).roundToInt(), temperature = weatherData?.temperature?.roundToInt() ?: 0,
temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS, temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS,
timeLabel = formattedTime, timeLabel = formattedTime,
dateLabel = if (hasNewDate) formattedDate else null, dateLabel = if (hasNewDate) formattedDate else null,
distance = if (settingsAndProfile.settings.showDistanceInForecast) distanceFromCurrent else null, distance = if (settingsAndProfile.settings.showDistanceInForecast) distanceFromCurrent else null,
isImperial = settingsAndProfile.isImperial, isImperial = settingsAndProfile.isImperial
isNight = weatherData?.isNight == true
) )
previousDate = formattedDate previousDate = formattedDate

View File

@ -34,16 +34,16 @@ fun GraphicalForecast(
distance: Double? = null, distance: Double? = null,
timeLabel: String? = null, timeLabel: String? = null,
rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally, rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally,
isImperial: Boolean?, isImperial: Boolean?
isNight: Boolean,
) { ) {
Column(modifier = GlanceModifier.fillMaxHeight().padding(1.dp).width(86.dp), horizontalAlignment = rowAlignment) { Column(modifier = GlanceModifier.fillMaxHeight().padding(1.dp).width(86.dp), horizontalAlignment = rowAlignment) {
Row(modifier = GlanceModifier.defaultWeight().wrapContentWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) { Row(modifier = GlanceModifier.defaultWeight().wrapContentWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) {
Image( Image(
modifier = GlanceModifier.defaultWeight().wrapContentWidth().padding(1.dp), modifier = GlanceModifier.defaultWeight().wrapContentWidth().padding(1.dp),
provider = ImageProvider(getWeatherIcon(current, isNight)), provider = ImageProvider(getWeatherIcon(current)),
contentDescription = "Current weather information", contentDescription = "Current weather information",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
) )
} }
@ -96,15 +96,13 @@ class GraphicalForecastDataType(karooSystem: KarooSystemService) : ForecastDataT
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean,
) { ) {
GraphicalForecast( GraphicalForecast(
current = current, current = current,
distance = distance, distance = distance,
timeLabel = timeLabel, timeLabel = timeLabel,
isImperial = isImperial, isImperial = isImperial
isNight = isNight
) )
} }
} }

View File

@ -12,11 +12,8 @@ import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.WindDirectionIndicatorSetting import de.timklge.karooheadwind.WindDirectionIndicatorSetting
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.util.msInUserUnit
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
@ -26,7 +23,6 @@ import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.HardwareType import io.hammerhead.karooext.models.HardwareType
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
import io.hammerhead.karooext.models.UpdateGraphicConfig import io.hammerhead.karooext.models.UpdateGraphicConfig
import io.hammerhead.karooext.models.UserProfile
import io.hammerhead.karooext.models.ViewConfig import io.hammerhead.karooext.models.ViewConfig
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -35,7 +31,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -48,54 +43,46 @@ class HeadwindDirectionDataType(
) : DataTypeImpl("karoo-headwind", "headwind") { ) : DataTypeImpl("karoo-headwind", "headwind") {
private val glance = GlanceRemoteViews() private val glance = GlanceRemoteViews()
data class StreamData(val headingResponse: HeadingResponse, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings)
private fun streamValues(): Flow<Double> = flow { private fun streamValues(): Flow<Double> = flow {
combine( karooSystem.getRelativeHeadingFlow(applicationContext)
karooSystem.getRelativeHeadingFlow(applicationContext), .combine(applicationContext.streamCurrentWeatherData(karooSystem)) { headingResponse, data ->
applicationContext.streamCurrentWeatherData(karooSystem), StreamData(headingResponse, data?.windDirection, data?.windSpeed)
applicationContext.streamSettings(karooSystem),
) { headingResponse, currentWeather, settings ->
StreamData(
headingResponse,
currentWeather?.windDirection,
currentWeather?.windSpeed,
settings,
)
}.collect { streamData ->
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
var returnValue = 0.0
if (value == null || streamData.absoluteWindDirection == null || streamData.windSpeed == null){
var errorCode = 1.0
var headingResponse = streamData.headingResponse
if (headingResponse is HeadingResponse.Value && (streamData.absoluteWindDirection == null || streamData.windSpeed == null)){
headingResponse = HeadingResponse.NoWeatherData
} }
.combine(applicationContext.streamSettings(karooSystem)) { data, settings -> data.copy(settings = settings) }
.collect { streamData ->
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
if (streamData.settings.welcomeDialogAccepted == false){ var returnValue = 0.0
errorCode = ERROR_APP_NOT_SET_UP.toDouble() if (value == null || streamData.absoluteWindDirection == null || streamData.settings == null || streamData.windSpeed == null){
} else if (headingResponse is HeadingResponse.NoGps){ var errorCode = 1.0
errorCode = ERROR_NO_GPS.toDouble() var headingResponse = streamData.headingResponse
if (headingResponse is HeadingResponse.Value && (streamData.absoluteWindDirection == null || streamData.windSpeed == null)){
headingResponse = HeadingResponse.NoWeatherData
}
if (streamData.settings?.welcomeDialogAccepted == false){
errorCode = ERROR_APP_NOT_SET_UP.toDouble()
} else if (headingResponse is HeadingResponse.NoGps){
errorCode = ERROR_NO_GPS.toDouble()
} else {
errorCode = ERROR_NO_WEATHER_DATA.toDouble()
}
returnValue = errorCode
} else { } else {
errorCode = ERROR_NO_WEATHER_DATA.toDouble() var windDirection = when (streamData.settings.windDirectionIndicatorSetting){
WindDirectionIndicatorSetting.HEADWIND_DIRECTION -> value
WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180
}
if (windDirection < 0) windDirection += 360
returnValue = windDirection
} }
returnValue = errorCode emit(returnValue)
} else {
var windDirection = when (streamData.settings.windDirectionIndicatorSetting){
WindDirectionIndicatorSetting.HEADWIND_DIRECTION -> value
WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180
}
if (windDirection < 0) windDirection += 360
returnValue = windDirection
} }
emit(returnValue)
}
} }
override fun startStream(emitter: Emitter<StreamState>) { override fun startStream(emitter: Emitter<StreamState>) {
@ -109,12 +96,9 @@ class HeadwindDirectionDataType(
} }
} }
data class DirectionAndSpeed( data class StreamData(val headingResponse: HeadingResponse?, val absoluteWindDirection: Double?, val windSpeed: Double?, val settings: HeadwindSettings? = null)
val bearing: Double,
val speed: Double?, data class DirectionAndSpeed(val bearing: Double, val speed: Double?)
val isVisible: Boolean,
val isImperial: Boolean
)
private fun previewFlow(): Flow<DirectionAndSpeed> { private fun previewFlow(): Flow<DirectionAndSpeed> {
return flow { return flow {
@ -122,12 +106,7 @@ class HeadwindDirectionDataType(
val bearing = (0..360).random().toDouble() val bearing = (0..360).random().toDouble()
val windSpeed = (0..20).random() val windSpeed = (0..20).random()
emit(DirectionAndSpeed( emit(DirectionAndSpeed(bearing, windSpeed.toDouble()))
bearing,
windSpeed.toDouble(),
true,
true
))
delay(2_000) delay(2_000)
} }
@ -157,15 +136,15 @@ class HeadwindDirectionDataType(
emitAll(UserWindSpeedDataType.streamValues(context, karooSystem)) emitAll(UserWindSpeedDataType.streamValues(context, karooSystem))
} }
combine(directionFlow, speedFlow, karooSystem.streamDatatypeIsVisible(dataTypeId), karooSystem.streamUserProfile()) { direction, speed, isVisible, profile -> combine(directionFlow, speedFlow) { direction, speed ->
DirectionAndSpeed(direction, speed, isVisible, profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL) DirectionAndSpeed(direction, speed)
} }
} }
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
flow.filter { it.isVisible }.throttle(refreshRate).collect { streamData -> flow.throttle(refreshRate).collect { streamData ->
Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view") Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null } val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null }
@ -175,15 +154,14 @@ class HeadwindDirectionDataType(
} }
val windDirection = streamData.bearing val windDirection = streamData.bearing
val windSpeed = streamData.speed ?: 0.0 val windSpeed = streamData.speed
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial)
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
HeadwindDirection( HeadwindDirection(
baseBitmap, baseBitmap,
windDirection.roundToInt(), windDirection.roundToInt(),
config.textSize, config.textSize,
windSpeedUserUnit.roundToInt().toString(), windSpeed?.toInt()?.toString() ?: "",
preview = config.preview, preview = config.preview,
wideMode = false wideMode = false
) )

View File

@ -67,7 +67,7 @@ fun HeadwindDirection(
val baseModifier = GlanceModifier.fillMaxSize().padding(5.dp).background(dayColor, nightColor).cornerRadius(10.dp) val baseModifier = GlanceModifier.fillMaxSize().padding(5.dp).background(dayColor, nightColor).cornerRadius(10.dp)
Box( Box(
modifier = baseModifier, // TODO if (!preview) baseModifier.clickable(actionStartActivity<MainActivity>()) else baseModifier, modifier = if (!preview) baseModifier.clickable(actionStartActivity<MainActivity>()) else baseModifier,
contentAlignment = Alignment( contentAlignment = Alignment(
vertical = Alignment.Vertical.CenterVertically, vertical = Alignment.Vertical.CenterVertically,
horizontal = Alignment.Horizontal.CenterHorizontally, horizontal = Alignment.Horizontal.CenterHorizontally,

View File

@ -7,16 +7,12 @@ import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.throttle
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
import io.hammerhead.karooext.internal.ViewEmitter
import io.hammerhead.karooext.models.DataPoint import io.hammerhead.karooext.models.DataPoint
import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
import io.hammerhead.karooext.models.UpdateGraphicConfig
import io.hammerhead.karooext.models.ViewConfig
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -31,14 +27,11 @@ class HeadwindSpeedDataType(
override fun startStream(emitter: Emitter<StreamState>) { override fun startStream(emitter: Emitter<StreamState>) {
val job = CoroutineScope(Dispatchers.IO).launch { val job = CoroutineScope(Dispatchers.IO).launch {
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
karooSystem.getRelativeHeadingFlow(context) karooSystem.getRelativeHeadingFlow(context)
.combine(context.streamCurrentWeatherData(karooSystem)) { value, data -> value to data } .combine(context.streamCurrentWeatherData(karooSystem)) { value, data -> value to data }
.combine(context.streamSettings(karooSystem)) { (value, data), settings -> .combine(context.streamSettings(karooSystem)) { (value, data), settings ->
StreamData(value, data, settings) StreamData(value, data, settings)
} }
.throttle(refreshRate)
.collect { streamData -> .collect { streamData ->
val windSpeed = streamData.weatherData?.windSpeed ?: 0.0 val windSpeed = streamData.weatherData?.windSpeed ?: 0.0
val windDirection = (streamData.headingResponse as? HeadingResponse.Value)?.diff ?: 0.0 val windDirection = (streamData.headingResponse as? HeadingResponse.Value)?.diff ?: 0.0
@ -52,9 +45,5 @@ class HeadwindSpeedDataType(
job.cancel() job.cancel()
} }
} }
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
emitter.onNext(UpdateGraphicConfig(formatDataTypeId = DataType.Type.SPEED))
}
} }

View File

@ -1,13 +1,11 @@
package de.timklge.karooheadwind.datatypes package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.util.millimetersInUserUnit
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class PrecipitationDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "precipitation"){ class PrecipitationDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "precipitation"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double { override fun getValue(data: WeatherData): Double {
return millimetersInUserUnit(data.precipitation, userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL) return data.precipitation
} }
} }

View File

@ -25,6 +25,7 @@ import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.roundToInt
@Composable @Composable
fun PrecipitationForecast( fun PrecipitationForecast(
@ -33,7 +34,7 @@ fun PrecipitationForecast(
distance: Double? = null, distance: Double? = null,
timeLabel: String? = null, timeLabel: String? = null,
rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally, rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally,
isImperial: Boolean?, isImperial: Boolean?
) { ) {
Column(modifier = GlanceModifier.fillMaxHeight().padding(1.dp).width(86.dp), horizontalAlignment = rowAlignment) { Column(modifier = GlanceModifier.fillMaxHeight().padding(1.dp).width(86.dp), horizontalAlignment = rowAlignment) {
Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) { Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) {
@ -94,15 +95,14 @@ class PrecipitationForecastDataType(karooSystem: KarooSystemService) : ForecastD
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean,
) { ) {
PrecipitationForecast( PrecipitationForecast(
precipitation = ceil(precipitation).toInt(), precipitation = ceil(precipitation).toInt(),
precipitationProbability = precipitationProbability, precipitationProbability = precipitationProbability,
distance = distance, distance = distance,
timeLabel = timeLabel, timeLabel = timeLabel,
isImperial = isImperial, isImperial = isImperial
) )
} }
} }

View File

@ -10,7 +10,6 @@ import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
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
@ -114,7 +113,7 @@ class RelativeGradeDataType(private val karooSystemService: KarooSystemService,
return relativeGrade return relativeGrade
} }
suspend fun streamRelativeGrade(karooSystemService: KarooSystemService, context: Context): Flow<RelativeGradeResponse> { fun streamRelativeGrade(karooSystemService: KarooSystemService, context: Context): Flow<RelativeGradeResponse> {
val relativeWindDirectionFlow = karooSystemService.getRelativeHeadingFlow(context).filterIsInstance<HeadingResponse.Value>().map { it.diff + 180 } val relativeWindDirectionFlow = karooSystemService.getRelativeHeadingFlow(context).filterIsInstance<HeadingResponse.Value>().map { it.diff + 180 }
val speedFlow = karooSystemService.streamDataFlow(DataType.Type.SPEED).filterIsInstance<StreamState.Streaming>().map { it.dataPoint.singleValue ?: 0.0 } val speedFlow = karooSystemService.streamDataFlow(DataType.Type.SPEED).filterIsInstance<StreamState.Streaming>().map { it.dataPoint.singleValue ?: 0.0 }
val actualGradeFlow = karooSystemService.streamDataFlow(DataType.Type.ELEVATION_GRADE).filterIsInstance<StreamState.Streaming>().map { it.dataPoint.singleValue }.filterNotNull().map { it / 100.0 } // Convert to decimal grade val actualGradeFlow = karooSystemService.streamDataFlow(DataType.Type.ELEVATION_GRADE).filterIsInstance<StreamState.Streaming>().map { it.dataPoint.singleValue }.filterNotNull().map { it / 100.0 } // Convert to decimal grade
@ -127,10 +126,29 @@ class RelativeGradeDataType(private val karooSystemService: KarooSystemService,
} + DEFAULT_BIKE_WEIGHT } + DEFAULT_BIKE_WEIGHT
} }
val refreshRate = karooSystemService.getRefreshRateInMilliseconds(context) val windSpeedFlow = combine(context.streamSettings(karooSystemService), karooSystemService.streamUserProfile(), context.streamCurrentWeatherData(karooSystemService).filterNotNull()) { settings, profile, weatherData ->
val isOpenMeteo = settings.weatherProvider == WeatherDataProvider.OPEN_METEO
val profileIsImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
val windSpeedFlow = context.streamCurrentWeatherData(karooSystemService).filterNotNull().map { weatherData -> if (isOpenMeteo) {
weatherData.windSpeed if (profileIsImperial) { // OpenMeteo returns wind speed in mph
val windSpeedInMilesPerHour = weatherData.windSpeed
windSpeedInMilesPerHour * 0.44704
} else { // Wind speed reported by openmeteo is in km/h
val windSpeedInKmh = weatherData.windSpeed
windSpeedInKmh * 0.277778
}
} else {
if (profileIsImperial) { // OpenWeatherMap returns wind speed in mph
val windSpeedInMilesPerHour = weatherData.windSpeed
windSpeedInMilesPerHour * 0.44704
} else { // Wind speed reported by openweathermap is in m/s
weatherData.windSpeed
}
}
} }
data class StreamValues( data class StreamValues(
@ -143,7 +161,7 @@ class RelativeGradeDataType(private val karooSystemService: KarooSystemService,
return combine(relativeWindDirectionFlow, speedFlow, windSpeedFlow, actualGradeFlow, totalMassFlow) { windDirection, speed, windSpeed, actualGrade, totalMass -> return combine(relativeWindDirectionFlow, speedFlow, windSpeedFlow, actualGradeFlow, totalMassFlow) { windDirection, speed, windSpeed, actualGrade, totalMass ->
StreamValues(windDirection, speed, windSpeed, actualGrade, totalMass) StreamValues(windDirection, speed, windSpeed, actualGrade, totalMass)
}.distinctUntilChanged().throttle(refreshRate).map { (windDirection, speed, windSpeed, actualGrade, totalMass) -> }.distinctUntilChanged().map { (windDirection, speed, windSpeed, actualGrade, totalMass) ->
val relativeGrade = estimateRelativeGrade(actualGrade, speed, windSpeed, windDirection, totalMass) val relativeGrade = estimateRelativeGrade(actualGrade, speed, windSpeed, windDirection, totalMass)
Log.d(KarooHeadwindExtension.TAG, "Relative grade: $relativeGrade - Wind Direction: $windDirection - Speed: $speed - Wind Speed: $windSpeed - Actual Grade: $actualGrade - Total Mass: $totalMass") Log.d(KarooHeadwindExtension.TAG, "Relative grade: $relativeGrade - Wind Direction: $windDirection - Speed: $speed - Wind Speed: $windSpeed - Actual Grade: $actualGrade - Total Mass: $totalMass")

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class RelativeHumidityDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "relativeHumidity"){ class RelativeHumidityDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "relativeHumidity"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? { override fun getValue(data: WeatherData): Double? {
return data.relativeHumidity.toDouble() return data.relativeHumidity
} }
} }

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class SealevelPressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "sealevelPressure"){ class SealevelPressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "sealevelPressure"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? { override fun getValue(data: WeatherData): Double? {
return data.sealevelPressure return data.sealevelPressure
} }
} }

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class SurfacePressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "surfacePressure"){ class SurfacePressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "surfacePressure"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? { override fun getValue(data: WeatherData): Double? {
return data.surfacePressure return data.surfacePressure
} }
} }

View File

@ -21,12 +21,9 @@ import de.timklge.karooheadwind.datatypes.TailwindDataType.StreamData
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.util.msInUserUnit
import de.timklge.karooheadwind.weatherprovider.WeatherData
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.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
@ -88,7 +85,7 @@ class TailwindAndRideSpeedDataType(
val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10) val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10)
val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial, isVisible = true)) emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial))
delay(2_000) delay(2_000)
} }
@ -116,27 +113,18 @@ class TailwindAndRideSpeedDataType(
val flow = if (config.preview) { val flow = if (config.preview) {
previewFlow(karooSystem.streamUserProfile()) previewFlow(karooSystem.streamUserProfile())
} else { } else {
combine(karooSystem.getRelativeHeadingFlow(context), combine(karooSystem.getRelativeHeadingFlow(context), context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), streamSpeedInMs()) { headingResponse, weatherData, settings, userProfile, rideSpeedInMs ->
context.streamCurrentWeatherData(karooSystem),
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
streamSpeedInMs(),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data ->
val headingResponse = data[0] as HeadingResponse
val weatherData = data[1] as? WeatherData
val settings = data[2] as HeadwindSettings
val userProfile = data[3] as UserProfile
val rideSpeedInMs = data[4] as Double
val isVisible = data[5] as Boolean
val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
val absoluteWindDirection = weatherData?.windDirection val absoluteWindDirection = weatherData?.windDirection
val windSpeed = weatherData?.windSpeed val windSpeed = weatherData?.windSpeed
val gustSpeed = weatherData?.windGusts val gustSpeed = weatherData?.windGusts
val rideSpeed = rideSpeedInMs val rideSpeed = if (isImperial){
rideSpeedInMs * 2.23694
} else {
rideSpeedInMs * 3.6
}
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible) StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed)
} }
} }
@ -166,21 +154,15 @@ class TailwindAndRideSpeedDataType(
WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180 WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180
} }
val rideSpeedInUserUnit = msInUserUnit(streamData.rideSpeed ?: 0.0, streamData.isImperial) val text = streamData.rideSpeed?.let { String.format(Locale.current.platformLocale, "%.1f", it) } ?: ""
val text = String.format(Locale.current.platformLocale, "%.1f", rideSpeedInUserUnit)
val wideMode = config.gridSize.first == 60 val wideMode = config.gridSize.first == 60
val gustSpeedInUserUnit = msInUserUnit(streamData.gustSpeed ?: 0.0, streamData.isImperial)
val gustSpeedAddon = if (wideMode) { val gustSpeedAddon = if (wideMode) {
"-${gustSpeedInUserUnit.roundToInt()}" "-${streamData.gustSpeed?.roundToInt() ?: 0}"
} else { } else {
"" ""
} }
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial)
val subtextWithSign = when (streamData.settings.windDirectionIndicatorTextSetting) { val subtextWithSign = when (streamData.settings.windDirectionIndicatorTextSetting) {
WindDirectionIndicatorTextSetting.HEADWIND_SPEED -> { WindDirectionIndicatorTextSetting.HEADWIND_SPEED -> {
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
@ -189,12 +171,9 @@ class TailwindAndRideSpeedDataType(
val sign = if (headwindSpeed < 0) "+" else { val sign = if (headwindSpeed < 0) "+" else {
if (headwindSpeed > 0) "-" else "" if (headwindSpeed > 0) "-" else ""
} }
"$sign${headwindSpeed.roundToInt().absoluteValue} ${windSpeed.roundToInt()}${gustSpeedAddon}"
val headwindSpeedUserUnit = msInUserUnit(headwindSpeed, streamData.isImperial)
"$sign${headwindSpeedUserUnit.roundToInt().absoluteValue} ${windSpeedUserUnit.roundToInt()}${gustSpeedAddon}"
} }
WindDirectionIndicatorTextSetting.WIND_SPEED -> "${windSpeedUserUnit.roundToInt()}${gustSpeedAddon}" WindDirectionIndicatorTextSetting.WIND_SPEED -> "${windSpeed.roundToInt()}${gustSpeedAddon}"
WindDirectionIndicatorTextSetting.NONE -> "" WindDirectionIndicatorTextSetting.NONE -> ""
} }
@ -203,7 +182,11 @@ class TailwindAndRideSpeedDataType(
if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) { if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) {
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
val windSpeedInKmh = headwindSpeed * 3.6 val windSpeedInKmh = if (streamData.isImperial == true){
headwindSpeed / 2.23694 * 3.6
} else {
headwindSpeed
}
dayColor = interpolateWindColor(windSpeedInKmh, false, context) dayColor = interpolateWindColor(windSpeedInKmh, false, context)
nightColor = interpolateWindColor(windSpeedInKmh, true, context) nightColor = interpolateWindColor(windSpeedInKmh, true, context)
} }

View File

@ -17,12 +17,9 @@ import de.timklge.karooheadwind.WindDirectionIndicatorTextSetting
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.util.msInUserUnit
import de.timklge.karooheadwind.weatherprovider.WeatherData
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.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
@ -38,7 +35,6 @@ import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -60,8 +56,7 @@ class TailwindDataType(
val settings: HeadwindSettings, val settings: HeadwindSettings,
val rideSpeed: Double?, val rideSpeed: Double?,
val gustSpeed: Double?, val gustSpeed: Double?,
val isImperial: Boolean, val isImperial: Boolean)
val isVisible: Boolean)
private fun previewFlow(profileFlow: Flow<UserProfile>): Flow<StreamData> { private fun previewFlow(profileFlow: Flow<UserProfile>): Flow<StreamData> {
return flow { return flow {
@ -74,7 +69,7 @@ class TailwindDataType(
val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10) val gustSpeed = windSpeed * ((10..40).random().toDouble() / 10)
val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL val isImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial, isVisible = true)) emit(StreamData(HeadingResponse.Value(bearing), bearing, windSpeed.toDouble(), HeadwindSettings(), rideSpeed, gustSpeed = gustSpeed, isImperial = isImperial))
delay(2_000) delay(2_000)
} }
@ -103,26 +98,18 @@ class TailwindDataType(
val flow = if (config.preview) { val flow = if (config.preview) {
previewFlow(karooSystem.streamUserProfile()) previewFlow(karooSystem.streamUserProfile())
} else { } else {
combine(karooSystem.getRelativeHeadingFlow(context), combine(karooSystem.getRelativeHeadingFlow(context), context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), streamSpeedInMs()) { headingResponse, weatherData, settings, userProfile, rideSpeedInMs ->
context.streamCurrentWeatherData(karooSystem),
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
streamSpeedInMs(),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data ->
val headingResponse = data[0] as HeadingResponse
val weatherData = data[1] as? WeatherData
val settings = data[2] as HeadwindSettings
val userProfile = data[3] as UserProfile
val rideSpeedInMs = data[4] as Double
val isVisible = data[5] as Boolean
val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL val isImperial = userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
val absoluteWindDirection = weatherData?.windDirection val absoluteWindDirection = weatherData?.windDirection
val windSpeed = weatherData?.windSpeed val windSpeed = weatherData?.windSpeed
val gustSpeed = weatherData?.windGusts val gustSpeed = weatherData?.windGusts
val rideSpeed = if (isImperial){
rideSpeedInMs * 2.23694
} else {
rideSpeedInMs * 3.6
}
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeedInMs, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible) StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed)
} }
} }
@ -130,7 +117,7 @@ class TailwindDataType(
emitter.onNext(ShowCustomStreamState("", null)) emitter.onNext(ShowCustomStreamState("", null))
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) val refreshRate = karooSystem.getRefreshRateInMilliseconds(context)
flow.filter { it.isVisible }.throttle(refreshRate).collect { streamData -> flow.throttle(refreshRate).collect { streamData ->
Log.d(KarooHeadwindExtension.TAG, "Updating tailwind direction view") Log.d(KarooHeadwindExtension.TAG, "Updating tailwind direction view")
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
@ -160,26 +147,24 @@ class TailwindDataType(
val sign = if (headwindSpeed < 0) "+" else { val sign = if (headwindSpeed < 0) "+" else {
if (headwindSpeed > 0) "-" else "" if (headwindSpeed > 0) "-" else ""
} }
"$sign${headwindSpeed.roundToInt().absoluteValue}"
val headwindSpeedUserUnit = msInUserUnit(headwindSpeed, streamData.isImperial)
"$sign${headwindSpeedUserUnit.roundToInt().absoluteValue}"
} }
WindDirectionIndicatorTextSetting.WIND_SPEED -> msInUserUnit(windSpeed, streamData.isImperial).roundToInt().toString() WindDirectionIndicatorTextSetting.WIND_SPEED -> windSpeed.roundToInt().toString()
WindDirectionIndicatorTextSetting.NONE -> "" WindDirectionIndicatorTextSetting.NONE -> ""
} }
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial) val subtext = "${windSpeed.roundToInt()}-${streamData.gustSpeed?.roundToInt()}"
val gustSpeedUserUnit = msInUserUnit(streamData.gustSpeed ?: 0.0, streamData.isImperial)
val subtext = "${windSpeedUserUnit.roundToInt()}-${gustSpeedUserUnit.roundToInt()}"
var dayColor = Color(ContextCompat.getColor(context, R.color.black)) var dayColor = Color(ContextCompat.getColor(context, R.color.black))
var nightColor = Color(ContextCompat.getColor(context, R.color.white)) var nightColor = Color(ContextCompat.getColor(context, R.color.white))
if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) { if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) {
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
val windSpeedInKmh = headwindSpeed * 3.6 val windSpeedInKmh = if (streamData.isImperial){
headwindSpeed / 2.23694 * 3.6
} else {
headwindSpeed
}
dayColor = interpolateWindColor(windSpeedInKmh, false, context) dayColor = interpolateWindColor(windSpeedInKmh, false, context)
nightColor = interpolateWindColor(windSpeedInKmh, true, context) nightColor = interpolateWindColor(windSpeedInKmh, true, context)
} }

View File

@ -3,15 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.UserProfile
class TemperatureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "temperature"){ class TemperatureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "temperature"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double { override fun getValue(data: WeatherData): Double {
return data.temperature return data.temperature
} }
override fun getFormatDataType(): String? {
return DataType.Type.TEMPERATURE
}
} }

View File

@ -91,15 +91,14 @@ class TemperatureForecastDataType(karooSystem: KarooSystemService) : ForecastDat
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean
) { ) {
TemperatureForecast( TemperatureForecast(
temperature = temperature, temperature = temperature,
temperatureUnit = temperatureUnit, temperatureUnit = temperatureUnit,
distance = distance, distance = distance,
timeLabel = timeLabel, timeLabel = timeLabel,
isImperial = isImperial, isImperial = isImperial
) )
} }
} }

View File

@ -16,19 +16,13 @@ import de.timklge.karooheadwind.HeadingResponse
import de.timklge.karooheadwind.HeadwindSettings import de.timklge.karooheadwind.HeadwindSettings
import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.MainActivity import de.timklge.karooheadwind.MainActivity
import de.timklge.karooheadwind.R
import de.timklge.karooheadwind.TemperatureUnit import de.timklge.karooheadwind.TemperatureUnit
import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.util.celciusInUserUnit
import de.timklge.karooheadwind.util.millimetersInUserUnit
import de.timklge.karooheadwind.util.msInUserUnit
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile
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
@ -46,7 +40,6 @@ import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.Instant import java.time.Instant
@ -69,10 +62,11 @@ class WeatherDataType(
val job = CoroutineScope(Dispatchers.IO).launch { val job = CoroutineScope(Dispatchers.IO).launch {
val currentWeatherData = applicationContext.streamCurrentWeatherData(karooSystem) val currentWeatherData = applicationContext.streamCurrentWeatherData(karooSystem)
currentWeatherData.collect { data -> currentWeatherData
Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data?.weatherCode}") .collect { data ->
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to (data?.weatherCode?.toDouble() ?: 0.0))))) Log.d(KarooHeadwindExtension.TAG, "Wind code: ${data?.weatherCode}")
} emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to (data?.weatherCode?.toDouble() ?: 0.0)))))
}
} }
emitter.setCancellable { emitter.setCancellable {
job.cancel() job.cancel()
@ -80,18 +74,14 @@ class WeatherDataType(
} }
data class StreamData(val data: WeatherData?, val settings: HeadwindSettings, data class StreamData(val data: WeatherData?, val settings: HeadwindSettings,
val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null, val profile: UserProfile? = null, val headingResponse: HeadingResponse? = null)
val isVisible: Boolean)
private fun previewFlow(): Flow<StreamData> = flow { private fun previewFlow(): Flow<StreamData> = flow {
while (true){ while (true){
emit(StreamData( emit(StreamData(
WeatherData( WeatherData(Instant.now().epochSecond, 0.0,
Instant.now().epochSecond, 0.0, 20.0, 50.0, 3.0, 0.0, 1013.25, 980.0, 15.0, 30.0, 30.0,
20, 50.0, 3.0, 0.0, 1013.25, 980.0, 15.0, 30.0, 30.0, WeatherInterpretation.getKnownWeatherCodes().random(), isForecast = false), HeadwindSettings()))
WeatherInterpretation.getKnownWeatherCodes().random(), isForecast = false,
isNight = listOf(true, false).random()
), HeadwindSettings(), isVisible = true))
delay(5_000) delay(5_000)
} }
@ -106,29 +96,21 @@ class WeatherDataType(
val baseBitmap = BitmapFactory.decodeResource( val baseBitmap = BitmapFactory.decodeResource(
context.resources, context.resources,
R.drawable.arrow_0 de.timklge.karooheadwind.R.drawable.arrow_0
) )
val dataFlow = if (config.preview){ val dataFlow = if (config.preview){
previewFlow() previewFlow()
} else { } else {
combine( combine(context.streamCurrentWeatherData(karooSystem), context.streamSettings(karooSystem), karooSystem.streamUserProfile(), karooSystem.getHeadingFlow(context)) { data, settings, profile, heading ->
context.streamCurrentWeatherData(karooSystem), StreamData(data, settings, profile, heading)
context.streamSettings(karooSystem),
karooSystem.streamUserProfile(),
karooSystem.getHeadingFlow(context),
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { data, settings, profile, heading, isVisible ->
StreamData(data, settings, profile, heading, isVisible)
} }
} }
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(ShowCustomStreamState("", null)) emitter.onNext(ShowCustomStreamState("", null))
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) dataFlow.collect { (data, settings, userProfile, headingResponse) ->
dataFlow.filter { it.isVisible }.throttle(refreshRate).collect { (data, settings, userProfile, headingResponse) ->
Log.d(KarooHeadwindExtension.TAG, "Updating weather view") Log.d(KarooHeadwindExtension.TAG, "Updating weather view")
if (data == null){ if (data == null){
@ -143,18 +125,18 @@ class WeatherDataType(
val result = glance.compose(context, DpSize.Unspecified) { val result = glance.compose(context, DpSize.Unspecified) {
var modifier = GlanceModifier.fillMaxSize() var modifier = GlanceModifier.fillMaxSize()
// TODO reenable once swipes are no longer interpreted as clicks if (!config.preview) modifier = modifier.clickable(onClick = actionStartActivity<MainActivity>()) if (!config.preview) modifier = modifier.clickable(onClick = actionStartActivity<MainActivity>())
Box(modifier = modifier, contentAlignment = Alignment.CenterEnd) { Box(modifier = modifier, contentAlignment = Alignment.CenterEnd) {
Weather( Weather(
baseBitmap, baseBitmap,
current = interpretation, current = interpretation,
windBearing = data.windDirection.roundToInt(), windBearing = data.windDirection.roundToInt(),
windSpeed = msInUserUnit(data.windSpeed, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windSpeed = data.windSpeed.roundToInt(),
windGusts = msInUserUnit(data.windGusts, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windGusts = data.windGusts.roundToInt(),
precipitation = millimetersInUserUnit(data.precipitation, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL), precipitation = data.precipitation,
precipitationProbability = null, precipitationProbability = null,
temperature = celciusInUserUnit(data.temperature, userProfile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), temperature = data.temperature.roundToInt(),
temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT, temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
timeLabel = formattedTime, timeLabel = formattedTime,
rowAlignment = when (config.alignment){ rowAlignment = when (config.alignment){
@ -164,8 +146,7 @@ class WeatherDataType(
}, },
dateLabel = formattedDate, dateLabel = formattedDate,
singleDisplay = true, singleDisplay = true,
isImperial = userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL, isImperial = userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
isNight = data.isNight,
) )
} }
} }

View File

@ -21,8 +21,7 @@ class WeatherForecastDataType(karooSystem: KarooSystemService) : ForecastDataTyp
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean,
) { ) {
Weather( Weather(
arrowBitmap = arrowBitmap, arrowBitmap = arrowBitmap,
@ -37,8 +36,7 @@ class WeatherForecastDataType(karooSystem: KarooSystemService) : ForecastDataTyp
timeLabel = timeLabel, timeLabel = timeLabel,
dateLabel = dateLabel, dateLabel = dateLabel,
distance = distance, distance = distance,
isImperial = isImperial, isImperial = isImperial
isNight = isNight,
) )
} }

View File

@ -45,14 +45,14 @@ fun getShortDateFormatter(): DateTimeFormatter = DateTimeFormatter.ofPattern(
} }
).withZone(ZoneId.systemDefault()) ).withZone(ZoneId.systemDefault())
fun getWeatherIcon(interpretation: WeatherInterpretation, isNight: Boolean): Int { fun getWeatherIcon(interpretation: WeatherInterpretation): Int {
return when (interpretation){ return when (interpretation){
WeatherInterpretation.CLEAR -> if (isNight) R.drawable.crescent_moon else R.drawable.sun WeatherInterpretation.CLEAR -> R.drawable.bx_clear
WeatherInterpretation.CLOUDY -> R.drawable.cloud WeatherInterpretation.CLOUDY -> R.drawable.bx_cloud
WeatherInterpretation.RAINY -> R.drawable.cloud_with_rain WeatherInterpretation.RAINY -> R.drawable.bx_cloud_rain
WeatherInterpretation.SNOWY -> R.drawable.cloud_with_snow WeatherInterpretation.SNOWY -> R.drawable.bx_cloud_snow
WeatherInterpretation.DRIZZLE -> R.drawable.cloud_with_light_rain WeatherInterpretation.DRIZZLE -> R.drawable.bx_cloud_drizzle
WeatherInterpretation.THUNDERSTORM -> R.drawable.cloud_with_lightning_and_rain WeatherInterpretation.THUNDERSTORM -> R.drawable.bx_cloud_lightning
WeatherInterpretation.UNKNOWN -> R.drawable.question_mark_regular_240 WeatherInterpretation.UNKNOWN -> R.drawable.question_mark_regular_240
} }
} }
@ -74,8 +74,7 @@ fun Weather(
rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally, rowAlignment: Alignment.Horizontal = Alignment.Horizontal.CenterHorizontally,
dateLabel: String? = null, dateLabel: String? = null,
singleDisplay: Boolean = false, singleDisplay: Boolean = false,
isImperial: Boolean?, isImperial: Boolean?
isNight: Boolean
) { ) {
val fontSize = if (singleDisplay) 19f else 14f val fontSize = if (singleDisplay) 19f else 14f
@ -84,9 +83,10 @@ fun Weather(
Row(modifier = GlanceModifier.defaultWeight().wrapContentWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) { Row(modifier = GlanceModifier.defaultWeight().wrapContentWidth(), horizontalAlignment = rowAlignment, verticalAlignment = Alignment.CenterVertically) {
Image( Image(
modifier = GlanceModifier.defaultWeight().wrapContentWidth().padding(1.dp), modifier = GlanceModifier.defaultWeight().wrapContentWidth().padding(1.dp),
provider = ImageProvider(getWeatherIcon(current, isNight)), provider = ImageProvider(getWeatherIcon(current)),
contentDescription = "Current weather information", contentDescription = "Current weather information",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
) )
} }
@ -142,6 +142,7 @@ fun Weather(
provider = ImageProvider(R.drawable.thermometer), provider = ImageProvider(R.drawable.thermometer),
contentDescription = "Temperature", contentDescription = "Temperature",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
) )
Text( Text(

View File

@ -17,25 +17,21 @@ 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.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamDatatypeIsVisible
import de.timklge.karooheadwind.throttle
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
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.ShowCustomStreamState import io.hammerhead.karooext.models.ShowCustomStreamState
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
import io.hammerhead.karooext.models.UpdateGraphicConfig import io.hammerhead.karooext.models.UpdateGraphicConfig
import io.hammerhead.karooext.models.UserProfile
import io.hammerhead.karooext.models.ViewConfig import io.hammerhead.karooext.models.ViewConfig
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -52,22 +48,14 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
) )
} }
override fun getValue(data: WeatherData, userProfile: UserProfile): Double { override fun getValue(data: WeatherData): Double {
return data.windDirection return data.windDirection
} }
data class StreamData( private fun previewFlow(): Flow<Double> {
val windBearing: Double,
val isVisible: Boolean
)
private fun previewFlow(): Flow<StreamData> {
return flow { return flow {
while (true) { while (true) {
emit(StreamData( emit((0..360).random().toDouble())
(0..360).random().toDouble(),
true
))
delay(1_000) delay(1_000)
} }
} }
@ -86,39 +74,31 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
val flow = if (config.preview){ val flow = if (config.preview){
previewFlow() previewFlow()
} else { } else {
combine( karooSystem.streamDataFlow(dataTypeId)
karooSystem.streamDataFlow(dataTypeId), .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue ?: 0.0 }
karooSystem.streamDatatypeIsVisible(dataTypeId)
) { windBearing, isVisible ->
StreamData(
windBearing = (windBearing as? StreamState.Streaming)?.dataPoint?.singleValue ?: 0.0,
isVisible = isVisible
)
}
} }
val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) flow
.collect { windBearing ->
val windCardinalDirectionIndex = ((windBearing % 360) / 22.5).roundToInt() % 16
flow.filter { it.isVisible }.throttle(refreshRate).collect { (windBearing, isVisible) -> val text = windDirections[windCardinalDirectionIndex]
val windCardinalDirectionIndex = ((windBearing % 360) / 22.5).roundToInt() % 16 Log.d( KarooHeadwindExtension.TAG,"Updating wind direction view")
val result = glance.compose(context, DpSize.Unspecified) {
val text = windDirections[windCardinalDirectionIndex] Box(modifier = GlanceModifier.fillMaxSize(),
Log.d(KarooHeadwindExtension.TAG,"Updating wind direction view") contentAlignment = Alignment(
val result = glance.compose(context, DpSize.Unspecified) { vertical = Alignment.Vertical.Top,
Box(modifier = GlanceModifier.fillMaxSize(), horizontal = when(config.alignment){
contentAlignment = Alignment( ViewConfig.Alignment.LEFT -> Alignment.Horizontal.Start
vertical = Alignment.Vertical.Top, ViewConfig.Alignment.CENTER -> Alignment.Horizontal.CenterHorizontally
horizontal = when(config.alignment){ ViewConfig.Alignment.RIGHT -> Alignment.Horizontal.End
ViewConfig.Alignment.LEFT -> Alignment.Horizontal.Start },
ViewConfig.Alignment.CENTER -> Alignment.Horizontal.CenterHorizontally )) {
ViewConfig.Alignment.RIGHT -> Alignment.Horizontal.End Text(text, style = TextStyle(color = ColorProvider(Color.Black, Color.White), fontFamily = FontFamily.Monospace, fontSize = TextUnit(
}, config.textSize.toFloat(), TextUnitType.Sp)))
)) { }
Text(text, style = TextStyle(color = ColorProvider(Color.Black, Color.White), fontFamily = FontFamily.Monospace, fontSize = TextUnit(
config.textSize.toFloat(), TextUnitType.Sp)))
} }
} emitter.updateView(result.remoteViews)
emitter.updateView(result.remoteViews)
} }
} }

View File

@ -103,8 +103,7 @@ class WindForecastDataType(karooSystem: KarooSystemService) : ForecastDataType(k
timeLabel: String, timeLabel: String,
dateLabel: String?, dateLabel: String?,
distance: Double?, distance: Double?,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean
) { ) {
WindForecast( WindForecast(
arrowBitmap = arrowBitmap, arrowBitmap = arrowBitmap,
@ -113,7 +112,7 @@ class WindForecastDataType(karooSystem: KarooSystemService) : ForecastDataType(k
gustSpeed = windGusts, gustSpeed = windGusts,
distance = distance, distance = distance,
timeLabel = timeLabel, timeLabel = timeLabel,
isImperial = isImperial, isImperial = isImperial
) )
} }
} }

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class WindGustsDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windGusts"){ class WindGustsDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windGusts"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double { override fun getValue(data: WeatherData): Double {
return data.windGusts return data.windGusts
} }
} }

View File

@ -3,10 +3,9 @@ package de.timklge.karooheadwind.datatypes
import android.content.Context import android.content.Context
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile
class WindSpeedDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windSpeed"){ class WindSpeedDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windSpeed"){
override fun getValue(data: WeatherData, userProfile: UserProfile): Double { override fun getValue(data: WeatherData): Double {
return data.windSpeed return data.windSpeed
} }
} }

View File

@ -84,7 +84,6 @@ fun SettingsScreen(onFinish: () -> Unit) {
var selectedWeatherProvider by remember { mutableStateOf(WeatherDataProvider.OPEN_METEO) } var selectedWeatherProvider by remember { mutableStateOf(WeatherDataProvider.OPEN_METEO) }
var openWeatherMapApiKey by remember { mutableStateOf("") } var openWeatherMapApiKey by remember { mutableStateOf("") }
var isK2 by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
ctx.streamSettings(karooSystem).collect { settings -> ctx.streamSettings(karooSystem).collect { settings ->
@ -103,7 +102,6 @@ fun SettingsScreen(onFinish: () -> Unit) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
karooSystem.connect { connected -> karooSystem.connect { connected ->
karooConnected = connected karooConnected = connected
isK2 = karooSystem.hardwareType == io.hammerhead.karooext.models.HardwareType.K2
} }
} }
@ -153,8 +151,8 @@ fun SettingsScreen(onFinish: () -> Unit) {
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
val refreshRateDropdownOptions = remember(isK2) { RefreshRate.entries.toList().map { unit -> DropdownOption(unit.id, unit.getDescription(isK2)) } } val refreshRateDropdownOptions = RefreshRate.entries.toList().map { unit -> DropdownOption(unit.id, unit.getDescription(karooSystem)) }
val refreshRateSelection by remember(refreshRateSetting, isK2) { val refreshRateSelection by remember(refreshRateSetting) {
mutableStateOf(refreshRateDropdownOptions.find { option -> option.id == refreshRateSetting.id }!!) mutableStateOf(refreshRateDropdownOptions.find { option -> option.id == refreshRateSetting.id }!!)
} }
Dropdown( Dropdown(
@ -162,7 +160,8 @@ fun SettingsScreen(onFinish: () -> Unit) {
options = refreshRateDropdownOptions, options = refreshRateDropdownOptions,
selected = refreshRateSelection selected = refreshRateSelection
) { selectedOption -> ) { selectedOption ->
refreshRateSetting = RefreshRate.entries.find { unit -> unit.id == selectedOption.id }!! refreshRateSetting =
RefreshRate.entries.find { unit -> unit.id == selectedOption.id }!!
} }
val windDirectionIndicatorSettingDropdownOptions = val windDirectionIndicatorSettingDropdownOptions =

View File

@ -37,9 +37,6 @@ import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamStats import de.timklge.karooheadwind.streamStats
import de.timklge.karooheadwind.streamUpcomingRoute import de.timklge.karooheadwind.streamUpcomingRoute
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.util.celciusInUserUnit
import de.timklge.karooheadwind.util.millimetersInUserUnit
import de.timklge.karooheadwind.util.msInUserUnit
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile import io.hammerhead.karooext.models.UserProfile
import java.time.Instant import java.time.Instant
@ -113,17 +110,16 @@ fun WeatherScreen(onFinish: () -> Unit) {
baseBitmap = baseBitmap, baseBitmap = baseBitmap,
current = WeatherInterpretation.fromWeatherCode(currentWeatherData?.weatherCode), current = WeatherInterpretation.fromWeatherCode(currentWeatherData?.weatherCode),
windBearing = currentWeatherData?.windDirection?.roundToInt() ?: 0, windBearing = currentWeatherData?.windDirection?.roundToInt() ?: 0,
windSpeed = msInUserUnit(currentWeatherData?.windSpeed ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windSpeed = currentWeatherData?.windSpeed?.roundToInt() ?: 0,
windGusts = msInUserUnit(currentWeatherData?.windGusts ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windGusts = currentWeatherData?.windGusts?.roundToInt() ?: 0,
precipitation = millimetersInUserUnit(currentWeatherData?.precipitation ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL), precipitation = currentWeatherData?.precipitation ?: 0.0,
temperature = celciusInUserUnit(currentWeatherData?.temperature ?: 0.0, profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), temperature = currentWeatherData?.temperature?.toInt() ?: 0,
temperatureUnit = if(profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.METRIC) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT, temperatureUnit = if(profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.METRIC) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
timeLabel = formattedTime, timeLabel = formattedTime,
dateLabel = formattedDate, dateLabel = formattedDate,
distance = requestedWeatherPosition?.let { l -> location?.distanceTo(l)?.times(1000) }, distance = requestedWeatherPosition?.let { l -> location?.distanceTo(l)?.times(1000) },
includeDistanceLabel = false, includeDistanceLabel = false,
isImperial = profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL, isImperial = profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL,
isNight = currentWeatherData?.isNight == true
) )
} }
@ -233,18 +229,17 @@ fun WeatherScreen(onFinish: () -> Unit) {
baseBitmap, baseBitmap,
current = interpretation, current = interpretation,
windBearing = weatherData?.windDirection?.roundToInt() ?: 0, windBearing = weatherData?.windDirection?.roundToInt() ?: 0,
windSpeed = msInUserUnit(currentWeatherData?.windSpeed ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windSpeed = weatherData?.windSpeed?.roundToInt() ?: 0,
windGusts = msInUserUnit(currentWeatherData?.windGusts ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), windGusts = weatherData?.windGusts?.roundToInt() ?: 0,
precipitation = millimetersInUserUnit(weatherData?.precipitation ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL), precipitation = weatherData?.precipitation ?: 0.0,
temperature = celciusInUserUnit(weatherData?.temperature ?: 0.0, profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(), temperature = weatherData?.temperature?.toInt() ?: 0,
temperatureUnit = if (profile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT, temperatureUnit = if (profile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
timeLabel = formattedForecastTime, timeLabel = formattedForecastTime,
dateLabel = formattedForecastDate, dateLabel = formattedForecastDate,
distance = distanceFromCurrent, distance = distanceFromCurrent,
includeDistanceLabel = true, includeDistanceLabel = true,
precipitationProbability = weatherData?.precipitationProbability?.toInt() ?: 0, precipitationProbability = weatherData?.precipitationProbability?.toInt() ?: 0,
isImperial = profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL, isImperial = profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
isNight = weatherData?.isNight == true,
) )
} }

View File

@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -47,10 +46,9 @@ fun WeatherWidget(
distance: Double? = null, distance: Double? = null,
includeDistanceLabel: Boolean = false, includeDistanceLabel: Boolean = false,
precipitationProbability: Int? = null, precipitationProbability: Int? = null,
isImperial: Boolean, isImperial: Boolean
isNight: Boolean
) { ) {
val fontSize = 18.sp val fontSize = 20.sp
Row( Row(
modifier = Modifier.fillMaxWidth().padding(5.dp), modifier = Modifier.fillMaxWidth().padding(5.dp),
@ -99,20 +97,22 @@ fun WeatherWidget(
} }
} }
Image( // Weather icon (larger)
painter = painterResource(id = getWeatherIcon(current, isNight)), Icon(
painter = painterResource(id = getWeatherIcon(current)),
contentDescription = "Current weather", contentDescription = "Current weather",
modifier = Modifier.size(72.dp) modifier = Modifier.size(72.dp)
) )
Column(horizontalAlignment = Alignment.End) { Column(horizontalAlignment = Alignment.End) {
// Temperature (larger)
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Image( Icon(
painter = painterResource(id = R.drawable.thermometer), painter = painterResource(id = R.drawable.thermometer),
contentDescription = "Temperature", contentDescription = "Temperature",
modifier = Modifier.size(18.dp), modifier = Modifier.size(18.dp)
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
@ -134,8 +134,8 @@ fun WeatherWidget(
val precipitationProbabilityLabel = val precipitationProbabilityLabel =
if (precipitationProbability != null) "${precipitationProbability}% " else "" if (precipitationProbability != null) "${precipitationProbability}% " else ""
Image( Icon(
painter = painterResource(id = R.drawable.droplet), painter = painterResource(id = R.drawable.droplet_regular),
contentDescription = "Precipitation", contentDescription = "Precipitation",
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )

View File

@ -1,26 +0,0 @@
package de.timklge.karooheadwind.util
fun celciusInUserUnit(celcius: Double, isImperial: Boolean): Double {
return if (isImperial) {
celcius * 9.0 / 5 + 32.0
} else {
celcius
}
}
fun millimetersInUserUnit(millimeters: Double, isImperial: Boolean): Double {
return if (isImperial) {
millimeters / 25.4
} else {
millimeters
}
}
// Returns the given speed value (m / s) in user unit (km/h or mph)
fun msInUserUnit(ms: Double, isImperial: Boolean): Double {
return if (isImperial) {
ms * 2.2369362920544
} else {
ms * 3.6
}
}

View File

@ -6,17 +6,16 @@ import kotlinx.serialization.Serializable
data class WeatherData( data class WeatherData(
val time: Long, val time: Long,
val temperature: Double, val temperature: Double,
val relativeHumidity: Int, val relativeHumidity: Double? = null,
val precipitation: Double, val precipitation: Double,
val precipitationProbability: Double? = null, val precipitationProbability: Double? = null,
val cloudCover: Double, val cloudCover: Double? = null,
val sealevelPressure: Double, val sealevelPressure: Double? = null,
val surfacePressure: Double, val surfacePressure: Double? = null,
val windSpeed: Double, val windSpeed: Double,
val windDirection: Double, val windDirection: Double,
val windGusts: Double, val windGusts: Double,
val weatherCode: Int, val weatherCode: Int,
val isForecast: Boolean, val isForecast: Boolean
val isNight: Boolean
) )

View File

@ -12,16 +12,15 @@ data class OpenMeteoWeatherData(
@SerialName("precipitation") val precipitation: Double, @SerialName("precipitation") val precipitation: Double,
@SerialName("cloud_cover") val cloudCover: Int, @SerialName("cloud_cover") val cloudCover: Int,
@SerialName("surface_pressure") val surfacePressure: Double, @SerialName("surface_pressure") val surfacePressure: Double,
@SerialName("pressure_msl") val sealevelPressure: Double, @SerialName("pressure_msl") val sealevelPressure: Double? = null,
@SerialName("wind_speed_10m") val windSpeed: Double, @SerialName("wind_speed_10m") val windSpeed: Double,
@SerialName("wind_direction_10m") val windDirection: Double, @SerialName("wind_direction_10m") val windDirection: Double,
@SerialName("wind_gusts_10m") val windGusts: Double, @SerialName("wind_gusts_10m") val windGusts: Double,
@SerialName("weather_code") val weatherCode: Int, @SerialName("weather_code") val weatherCode: Int,
@SerialName("is_day") val isDay: Int,
) { ) {
fun toWeatherData(): WeatherData = WeatherData( fun toWeatherData(): WeatherData = WeatherData(
temperature = temperature, temperature = temperature,
relativeHumidity = relativeHumidity, relativeHumidity = relativeHumidity.toDouble(),
precipitation = precipitation, precipitation = precipitation,
cloudCover = cloudCover.toDouble(), cloudCover = cloudCover.toDouble(),
surfacePressure = surfacePressure, surfacePressure = surfacePressure,
@ -32,7 +31,6 @@ data class OpenMeteoWeatherData(
weatherCode = weatherCode, weatherCode = weatherCode,
time = time, time = time,
isForecast = false, isForecast = false,
isNight = isDay == 0,
) )
} }

View File

@ -14,12 +14,7 @@ data class OpenMeteoWeatherForecastData(
@SerialName("wind_speed_10m") val windSpeed: List<Double>, @SerialName("wind_speed_10m") val windSpeed: List<Double>,
@SerialName("wind_direction_10m") val windDirection: List<Double>, @SerialName("wind_direction_10m") val windDirection: List<Double>,
@SerialName("wind_gusts_10m") val windGusts: List<Double>, @SerialName("wind_gusts_10m") val windGusts: List<Double>,
@SerialName("cloud_cover") val cloudCover: List<Double>, ) {
@SerialName("surface_pressure") val surfacePressure: List<Double>,
@SerialName("pressure_msl") val sealevelPressure: List<Double>,
@SerialName("is_day") val isDay: List<Int>,
@SerialName("relative_humidity_2m") val relativeHumidity: List<Int>,
) {
fun toWeatherData(): List<WeatherData> { fun toWeatherData(): List<WeatherData> {
return time.mapIndexed { index, t -> return time.mapIndexed { index, t ->
WeatherData( WeatherData(
@ -30,13 +25,8 @@ data class OpenMeteoWeatherForecastData(
windDirection = windDirection[index], windDirection = windDirection[index],
windGusts = windGusts[index], windGusts = windGusts[index],
weatherCode = weatherCode[index], weatherCode = weatherCode[index],
isNight = isDay[index] == 0,
time = t, time = t,
isForecast = true, isForecast = true,
cloudCover = cloudCover[index],
surfacePressure = surfacePressure[index],
sealevelPressure = sealevelPressure[index],
relativeHumidity = relativeHumidity[index],
) )
} }
} }

View File

@ -3,7 +3,10 @@ package de.timklge.karooheadwind.weatherprovider.openmeteo
import android.util.Log import android.util.Log
import de.timklge.karooheadwind.HeadwindSettings import de.timklge.karooheadwind.HeadwindSettings
import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.PrecipitationUnit
import de.timklge.karooheadwind.TemperatureUnit
import de.timklge.karooheadwind.WeatherDataProvider import de.timklge.karooheadwind.WeatherDataProvider
import de.timklge.karooheadwind.WindUnit
import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import de.timklge.karooheadwind.jsonWithUnknownKeys import de.timklge.karooheadwind.jsonWithUnknownKeys
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
@ -25,12 +28,16 @@ import kotlin.time.Duration.Companion.seconds
class OpenMeteoWeatherProvider : WeatherProvider { class OpenMeteoWeatherProvider : WeatherProvider {
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
private suspend fun makeOpenMeteoWeatherRequest(karooSystemService: KarooSystemService, gpsCoordinates: List<GpsCoordinates>): HttpResponseState.Complete { private suspend fun makeOpenMeteoWeatherRequest(karooSystemService: KarooSystemService, gpsCoordinates: List<GpsCoordinates>, settings: HeadwindSettings, profile: UserProfile?): HttpResponseState.Complete {
val precipitationUnit = if (profile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) PrecipitationUnit.MILLIMETERS else PrecipitationUnit.INCH
val temperatureUnit = if (profile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT
val windUnit = if (profile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) WindUnit.KILOMETERS_PER_HOUR else WindUnit.MILES_PER_HOUR
val response = callbackFlow { val response = callbackFlow {
// https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=is_day,surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m&timeformat=unixtime&past_hours=1&forecast_days=1&forecast_hours=12 // https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m&timeformat=unixtime&past_hours=1&forecast_days=1&forecast_hours=12
val lats = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lat) } val lats = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lat) }
val lons = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lon) } val lons = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lon) }
val url = "https://api.open-meteo.com/v1/forecast?latitude=${lats}&longitude=${lons}&current=is_day,surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m,is_day,surface_pressure,pressure_msl,relative_humidity_2m,cloud_cover&timeformat=unixtime&past_hours=0&forecast_days=1&forecast_hours=12&wind_speed_unit=ms" val url = "https://api.open-meteo.com/v1/forecast?latitude=${lats}&longitude=${lons}&current=surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m&timeformat=unixtime&past_hours=0&forecast_days=1&forecast_hours=12&wind_speed_unit=${windUnit.id}&precipitation_unit=${precipitationUnit.id}&temperature_unit=${temperatureUnit.id}"
Log.d(KarooHeadwindExtension.TAG, "Http request to ${url}...") Log.d(KarooHeadwindExtension.TAG, "Http request to ${url}...")
@ -77,7 +84,7 @@ class OpenMeteoWeatherProvider : WeatherProvider {
settings: HeadwindSettings, settings: HeadwindSettings,
profile: UserProfile? profile: UserProfile?
): WeatherDataResponse { ): WeatherDataResponse {
val openMeteoResponse = makeOpenMeteoWeatherRequest(karooSystem, coordinates) val openMeteoResponse = makeOpenMeteoWeatherRequest(karooSystem, coordinates, settings, profile)
val responseBody = openMeteoResponse.body?.let { String(it) } ?: throw WeatherProviderException(500, "Null response from OpenMeteo") val responseBody = openMeteoResponse.body?.let { String(it) } ?: throw WeatherProviderException(500, "Null response from OpenMeteo")
val weatherData = if (coordinates.size == 1) { val weatherData = if (coordinates.size == 1) {

View File

@ -2,8 +2,6 @@ package de.timklge.karooheadwind.weatherprovider.openweathermap
import de.timklge.karooheadwind.weatherprovider.WeatherData import de.timklge.karooheadwind.weatherprovider.WeatherData
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.time.Instant
import java.time.ZoneOffset
@Serializable @Serializable
data class OpenWeatherMapForecastData( data class OpenWeatherMapForecastData(
@ -22,31 +20,20 @@ data class OpenWeatherMapForecastData(
val snow: Snow? = null, val snow: Snow? = null,
val weather: List<Weather> val weather: List<Weather>
) { ) {
fun toWeatherData(currentWeatherData: OpenWeatherMapWeatherData): WeatherData { fun toWeatherData(): WeatherData = WeatherData(
val dtInstant = Instant.ofEpochSecond(dt) temperature = temp,
val sunriseInstant = Instant.ofEpochSecond(currentWeatherData.sunrise) relativeHumidity = humidity.toDouble(),
val sunsetInstant = Instant.ofEpochSecond(currentWeatherData.sunset) precipitation = rain?.h1 ?: 0.0,
cloudCover = clouds.toDouble(),
val dtTime = dtInstant.atZone(ZoneOffset.UTC).toLocalTime() surfacePressure = pressure.toDouble(),
val sunriseTime = sunriseInstant.atZone(ZoneOffset.UTC).toLocalTime() sealevelPressure = pressure.toDouble(), // FIXME
val sunsetTime = sunsetInstant.atZone(ZoneOffset.UTC).toLocalTime() windSpeed = wind_speed,
windDirection = wind_deg.toDouble(),
return WeatherData( windGusts = wind_gust ?: wind_speed,
temperature = temp, weatherCode = OpenWeatherMapWeatherProvider.convertWeatherCodeToOpenMeteo(
relativeHumidity = humidity, weather.firstOrNull()?.id ?: 800
precipitation = rain?.h1 ?: 0.0, ),
cloudCover = clouds.toDouble(), time = dt,
surfacePressure = pressure.toDouble(), isForecast = true
sealevelPressure = pressure.toDouble(), // FIXME )
windSpeed = wind_speed,
windDirection = wind_deg.toDouble(),
windGusts = wind_gust ?: wind_speed,
weatherCode = OpenWeatherMapWeatherProvider.convertWeatherCodeToOpenMeteo(
weather.firstOrNull()?.id ?: 800
),
time = dt,
isForecast = true,
isNight = dtTime.isBefore(sunriseTime) || dtTime.isAfter(sunsetTime)
)
}
} }

View File

@ -23,7 +23,7 @@ data class OpenWeatherMapWeatherData(
fun toWeatherData(): WeatherData = WeatherData( fun toWeatherData(): WeatherData = WeatherData(
temperature = temp, temperature = temp,
relativeHumidity = humidity, relativeHumidity = humidity.toDouble(),
precipitation = rain?.h1 ?: 0.0, precipitation = rain?.h1 ?: 0.0,
cloudCover = clouds.toDouble(), cloudCover = clouds.toDouble(),
surfacePressure = pressure.toDouble(), surfacePressure = pressure.toDouble(),
@ -35,9 +35,6 @@ data class OpenWeatherMapWeatherData(
weather.firstOrNull()?.id ?: 800 weather.firstOrNull()?.id ?: 800
), ),
time = dt, time = dt,
isNight = let {
dt !in sunrise..<sunset
},
isForecast = false isForecast = false
) )
} }

View File

@ -14,17 +14,10 @@ data class OpenWeatherMapWeatherDataForLocation(
val current: OpenWeatherMapWeatherData, val current: OpenWeatherMapWeatherData,
val hourly: List<OpenWeatherMapForecastData> val hourly: List<OpenWeatherMapForecastData>
){ ){
fun toWeatherDataForLocation(distanceAlongRoute: Double?): WeatherDataForLocation { fun toWeatherDataForLocation(distanceAlongRoute: Double?): WeatherDataForLocation = WeatherDataForLocation(
return WeatherDataForLocation( current = current.toWeatherData(),
current = current.toWeatherData(), coords = GpsCoordinates(lat, lon, bearing = null, distanceAlongRoute = distanceAlongRoute),
coords = GpsCoordinates( timezone = timezone,
lat, forecasts = hourly.map { it.toWeatherData() }
lon, )
bearing = null,
distanceAlongRoute = distanceAlongRoute
),
timezone = timezone,
forecasts = hourly.map { it.toWeatherData(current) }
)
}
} }

View File

@ -69,7 +69,7 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
profile: UserProfile? profile: UserProfile?
): WeatherDataResponse { ): WeatherDataResponse {
val response = makeOpenWeatherMapRequest(karooSystem, coordinates, apiKey) val response = makeOpenWeatherMapRequest(karooSystem, coordinates, apiKey, profile)
val responseBody = response.body?.let { String(it) } ?: throw Exception("Null response from OpenWeatherMap") val responseBody = response.body?.let { String(it) } ?: throw Exception("Null response from OpenWeatherMap")
val responses = mutableListOf<WeatherDataForLocation>() val responses = mutableListOf<WeatherDataForLocation>()
@ -89,15 +89,21 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
private suspend fun makeOpenWeatherMapRequest( private suspend fun makeOpenWeatherMapRequest(
service: KarooSystemService, service: KarooSystemService,
coordinates: List<GpsCoordinates>, coordinates: List<GpsCoordinates>,
apiKey: String apiKey: String,
profile: UserProfile?
): HttpResponseState.Complete { ): HttpResponseState.Complete {
val response = callbackFlow { val response = callbackFlow {
// OpenWeatherMap only supports setting imperial or metric units for all measurements, not individually for distance / temperature // OpenWeatherMap only supports setting imperial or metric units for all measurements, not individually for distance / temperature
val unitsString = if (profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL || profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL) {
"imperial"
} else {
"metric"
}
val coordinate = coordinates.first() val coordinate = coordinates.first()
// URL API 3.0 with onecall endpoint // URL API 3.0 with onecall endpoint
val url = "https://api.openweathermap.org/data/3.0/onecall?lat=${coordinate.lat}&lon=${coordinate.lon}" + val url = "https://api.openweathermap.org/data/3.0/onecall?lat=${coordinate.lat}&lon=${coordinate.lon}" +
"&appid=$apiKey&exclude=minutely,daily,alerts&units=metric" "&appid=$apiKey&exclude=minutely,daily,alerts&units=${unitsString}"
Log.d(KarooHeadwindExtension.TAG, "Http request to OpenWeatherMap API 3.0: $url") Log.d(KarooHeadwindExtension.TAG, "Http request to OpenWeatherMap API 3.0: $url")

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -1,40 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m106.54,56.4c-0.68,0 -1.35,0.05 -2.01,0.13 0.52,-1.75 0.81,-3.6 0.81,-5.52 0,-10.7 -8.67,-19.37 -19.37,-19.37 -4.55,0 -8.73,1.58 -12.04,4.21 -4.45,-8.44 -13.3,-14.2 -23.51,-14.2 -14.67,0 -26.56,11.89 -26.56,26.56 0,0.53 0.02,1.05 0.05,1.57 -11.06,1.36 -19.63,10.78 -19.63,22.2 0,12.22 9.81,22.15 21.98,22.36 4.08,7.08 12.2,11.94 21.59,12.01 9.08,0.07 17.03,-4.36 21.33,-11 3.47,3.79 8.44,6.19 13.99,6.19 7.94,0 14.74,-4.89 17.56,-11.81 1.82,0.65 3.76,1.03 5.8,1.03 9.49,0 17.18,-7.69 17.18,-17.18 0.01,-9.49 -7.68,-17.18 -17.17,-17.18z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="36.79"
android:centerY="18.87"
android:gradientRadius="114.63"
android:type="radial">
<item android:offset="0.14" android:color="#FFE3F2FD"/>
<item android:offset="1" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m47.86,105c-8.55,0 -16.33,-4.46 -20.3,-11.34 -0.26,-0.46 -0.75,-0.78 -1.27,-0.79 -11.31,-0.2 -20.51,-9.57 -20.51,-20.87 0,-10.52 7.87,-19.42 18.31,-20.7 0.79,-0.1 1.36,-0.79 1.31,-1.58 -0.03,-0.5 -0.05,-1 -0.05,-1.5 0,-13.82 11.24,-25.06 25.06,-25.06 9.33,0 17.83,5.13 22.18,13.4 0.21,0.4 0.58,0.68 1.02,0.77 0.1,0.02 0.21,0.03 0.31,0.03 0.34,0 0.67,-0.11 0.93,-0.33 3.2,-2.54 7.04,-3.89 11.11,-3.89 9.86,0 17.87,8.02 17.87,17.87 0,1.71 -0.25,3.42 -0.75,5.09 -0.14,0.48 -0.03,1.01 0.29,1.39 0.29,0.34 0.71,0.53 1.15,0.53 0.06,0 0.12,0 0.17,-0.01 0.68,-0.08 1.28,-0.12 1.83,-0.12 8.64,0 15.68,7.03 15.68,15.68s-7.03,15.68 -15.68,15.68c-1.78,0 -3.56,-0.32 -5.3,-0.94 -0.17,-0.06 -0.34,-0.09 -0.51,-0.09 -0.59,0 -1.15,0.35 -1.39,0.93 -2.7,6.61 -9.05,10.88 -16.18,10.88 -4.88,0 -9.58,-2.08 -12.89,-5.71 -0.29,-0.31 -0.69,-0.49 -1.11,-0.49h-0.12c-0.46,0.04 -0.88,0.36 -1.13,0.76 -4.13,6.37 -11.73,10.4 -19.85,10.4h-0.18z"
android:strokeAlpha="0.2"
android:fillAlpha="0.2">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="37.48"
android:centerY="20.46"
android:gradientRadius="111.26"
android:type="radial">
<item android:offset="0.14" android:color="#FFE3F2FD"/>
<item android:offset="1" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m50.42,24.65c8.77,0 16.76,4.83 20.85,12.6 0.42,0.79 1.16,1.35 2.04,1.54 0.2,0.04 0.41,0.06 0.62,0.06 0.67,0 1.33,-0.23 1.87,-0.65 2.93,-2.33 6.45,-3.56 10.17,-3.56 9.03,0 16.37,7.34 16.37,16.37 0,1.57 -0.23,3.14 -0.68,4.67 -0.29,0.97 -0.07,2.01 0.58,2.78 0.57,0.68 1.42,1.07 2.3,1.07 0.12,0 0.23,-0.01 0.35,-0.02 0.62,-0.07 1.16,-0.11 1.66,-0.11 7.82,0 14.18,6.36 14.18,14.18s-6.36,14.18 -14.18,14.18c-1.61,0 -3.22,-0.29 -4.79,-0.85 -0.33,-0.12 -0.68,-0.18 -1.01,-0.18 -1.19,0 -2.3,0.71 -2.78,1.87 -2.47,6.04 -8.27,9.95 -14.79,9.95 -4.46,0 -8.75,-1.9 -11.78,-5.22 -0.57,-0.63 -1.38,-0.98 -2.22,-0.98 -0.08,0 -0.16,0 -0.25,0.01 -0.93,0.08 -1.77,0.4 -2.27,1.18 -3.86,5.94 -10.98,9.46 -18.6,9.46h-0.18c-8.02,0 -15.31,-3.92 -19.01,-10.34 -0.53,-0.91 -1.49,-1.4 -2.55,-1.42 -10.5,-0.18 -19.04,-8.81 -19.04,-19.3 0,-9.76 7.3,-18 16.99,-19.18 1.57,-0.19 2.72,-1.56 2.63,-3.14 -0.03,-0.53 -0.05,-0.98 -0.05,-1.4 0.01,-13 10.58,-23.57 23.57,-23.57m0,-3c-14.67,0 -26.56,11.89 -26.56,26.56 0,0.53 0.02,1.06 0.05,1.58 -11.06,1.36 -19.63,10.77 -19.63,22.19 0,12.22 9.81,21.97 21.98,22.17 4.08,7.08 12.2,11.84 21.59,11.84h0.21c8.99,0 16.85,-4.25 21.12,-10.84 3.47,3.8 8.45,6.29 14,6.29 7.94,0 14.74,-4.84 17.56,-11.77 1.82,0.65 3.76,1.05 5.8,1.05 9.49,0 17.18,-7.68 17.18,-17.16s-7.69,-17.17 -17.18,-17.17c-0.68,0 -1.35,0.05 -2.01,0.13 0.52,-1.75 0.81,-3.6 0.81,-5.52 0,-10.7 -8.67,-19.37 -19.37,-19.37 -4.55,0 -8.73,1.58 -12.04,4.21 -4.45,-8.43 -13.31,-14.19 -23.51,-14.19z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,49 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m106.97,39.56c-0.68,0 -1.35,0.05 -2,0.13 0.52,-1.75 0.81,-3.59 0.81,-5.51 0,-10.68 -8.66,-19.34 -19.34,-19.34 -4.55,0 -8.72,1.58 -12.02,4.21 -4.45,-8.43 -13.29,-14.18 -23.48,-14.18 -14.64,0 -26.52,11.87 -26.52,26.52 0,0.53 0.02,1.05 0.05,1.57 -11.03,1.35 -19.58,10.75 -19.58,22.15 0,12.2 9.79,22.11 21.94,22.32 4.07,7.07 12.18,11.92 21.55,11.99 9.06,0.07 17,-4.35 21.3,-10.98 3.46,3.79 8.43,6.18 13.96,6.18 7.93,0 14.71,-4.88 17.53,-11.79 1.81,0.65 3.76,1.02 5.79,1.02 9.47,0 17.15,-7.68 17.15,-17.15 0,-9.46 -7.68,-17.14 -17.14,-17.14z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="59.47"
android:centerY="-5.18"
android:gradientRadius="120.22"
android:type="radial">
<item android:offset="0.26" android:color="#FFE3F2FD"/>
<item android:offset="0.92" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m50.94,7.87c8.75,0 16.73,4.82 20.81,12.57 0.42,0.79 1.16,1.35 2.04,1.54 0.2,0.04 0.41,0.06 0.62,0.06 0.67,0 1.33,-0.23 1.87,-0.65 2.92,-2.32 6.43,-3.55 10.15,-3.55 9.01,0 16.34,7.33 16.34,16.34 0,1.56 -0.23,3.13 -0.68,4.66 -0.29,0.97 -0.07,2.01 0.58,2.78 0.57,0.68 1.42,1.07 2.3,1.07 0.12,0 0.23,-0.01 0.35,-0.02 0.62,-0.07 1.16,-0.11 1.66,-0.11 7.8,0 14.15,6.35 14.15,14.15s-6.35,14.15 -14.15,14.15c-1.61,0 -3.21,-0.29 -4.78,-0.85 -0.33,-0.12 -0.68,-0.18 -1.01,-0.18 -1.19,0 -2.3,0.71 -2.78,1.87 -2.46,6.03 -8.25,9.92 -14.76,9.92 -4.45,0 -8.74,-1.9 -11.76,-5.21 -0.57,-0.63 -1.38,-0.98 -2.22,-0.98 -0.08,0 -0.16,0 -0.25,0.01 -0.93,0.08 -1.77,0.37 -2.27,1.15 -3.85,5.93 -10.96,9.41 -18.56,9.41h-0.19c-8.01,0 -15.28,-3.87 -18.97,-10.28 -0.53,-0.91 -1.49,-1.38 -2.55,-1.4 -10.47,-0.18 -18.99,-8.79 -18.99,-19.26 0,-9.74 7.29,-17.95 16.95,-19.14 1.57,-0.19 2.72,-1.56 2.63,-3.14 -0.03,-0.52 -0.05,-0.98 -0.05,-1.4 0.01,-12.96 10.56,-23.51 23.52,-23.51m0,-3c-14.64,0 -26.52,11.87 -26.52,26.52 0,0.53 0.02,1.06 0.05,1.58 -11.03,1.35 -19.58,10.74 -19.58,22.14 0,12.2 9.79,21.89 21.94,22.1 4.07,7.07 12.17,11.79 21.55,11.79h0.21c8.98,0 16.82,-4.2 21.08,-10.78 3.46,3.79 8.43,6.29 13.98,6.29 7.93,0 14.71,-4.82 17.53,-11.74 1.81,0.65 3.76,1.05 5.79,1.05 9.47,0 17.15,-7.66 17.15,-17.13s-7.68,-17.14 -17.15,-17.14c-0.68,0 -1.35,0.05 -2,0.13 0.52,-1.75 0.81,-3.59 0.81,-5.51 0,-10.68 -8.66,-19.34 -19.34,-19.34 -4.55,0 -8.72,1.58 -12.02,4.21 -4.45,-8.42 -13.29,-14.17 -23.48,-14.17z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m91.26,111.28c-1.78,3.88 -6.36,5.58 -10.24,3.8 -3.88,-1.78 -5.58,-6.36 -3.8,-10.24 1.78,-3.88 13.36,-11.57 13.36,-11.57 0,0 2.46,14.13 0.68,18.01z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m91.26,111.28c-1.78,3.88 -6.36,5.58 -10.24,3.8 -3.88,-1.78 -5.58,-6.36 -3.8,-10.24 1.78,-3.88 13.36,-11.57 13.36,-11.57 0,0 2.46,14.13 0.68,18.01z"
android:fillColor="#90caf9"/>
<path
android:pathData="m50.84,113.15c-1.78,3.88 -6.36,5.58 -10.24,3.8 -3.88,-1.78 -5.58,-6.36 -3.8,-10.24 1.78,-3.88 13.36,-11.57 13.36,-11.57 0,0 2.46,14.13 0.68,18.01z"
android:fillColor="#90caf9"/>
<path
android:pathData="m48.68,98.59c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -2.87,-1.32 -4.13,-4.72 -2.82,-7.59 0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45c0,0 -11.58,7.7 -13.36,11.57 -1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m89.1,96.72c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -1.39,-0.64 -2.45,-1.78 -2.98,-3.21 -0.53,-1.43 -0.47,-2.99 0.16,-4.38 0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45c0,0 -11.58,7.7 -13.36,11.57 -1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m89.1,96.72c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -1.39,-0.64 -2.45,-1.78 -2.98,-3.21 -0.53,-1.43 -0.47,-2.99 0.16,-4.38 0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45c0,0 -11.58,7.7 -13.36,11.57 -1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,44 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m51.04,121.76 l13.8,-24.27c0.39,-0.68 -0.1,-1.53 -0.89,-1.54l-26.19,-0.1c-0.95,0 -1.39,-1.17 -0.68,-1.8l34.24,-30.28c0.85,-0.75 2.12,0.23 1.6,1.24l-10.37,20.07c-0.35,0.68 0.14,1.5 0.91,1.5l26.78,0.17c0.94,0.01 1.38,1.16 0.68,1.79l-38.3,34.49c-0.87,0.79 -2.16,-0.25 -1.58,-1.27z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="59.01"
android:startY="73.87"
android:endX="71.67"
android:endY="118.12"
android:type="linear">
<item android:offset="0" android:color="#FFFBC02D"/>
<item android:offset="1" android:color="#FFFFEB3B"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m65.41,73.01 l-5.52,10.69c-0.65,1.25 -0.6,2.72 0.13,3.93s2.01,1.94 3.42,1.94l21.69,0.14 -25.47,22.93 7.78,-13.68c0.71,-1.24 0.7,-2.78 -0.02,-4.01 -0.71,-1.23 -2.05,-2 -3.47,-2.01l-21.01,-0.08zM71.99,63.51c-0.23,0 -0.46,0.08 -0.67,0.27l-34.24,30.28c-0.71,0.63 -0.27,1.8 0.68,1.8l26.19,0.1c0.79,0 1.28,0.85 0.89,1.54l-13.8,24.27c-0.44,0.77 0.19,1.55 0.9,1.55 0.23,0 0.47,-0.08 0.68,-0.27l38.3,-34.49c0.7,-0.63 0.26,-1.79 -0.68,-1.79l-26.78,-0.17c-0.77,-0.01 -1.26,-0.82 -0.91,-1.5l10.36,-20.06c0.4,-0.79 -0.22,-1.53 -0.92,-1.53z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m103.18,36.62c-0.63,0 -1.24,0.05 -1.85,0.12 0.48,-1.61 0.74,-3.32 0.74,-5.08 0,-9.85 -7.99,-17.84 -17.84,-17.84 -4.19,0 -8.04,1.46 -11.09,3.88 -4.09,-7.78 -12.25,-13.08 -21.65,-13.08 -13.51,0 -24.46,10.95 -24.46,24.46 0,0.49 0.02,0.97 0.05,1.45 -10.19,1.24 -18.07,9.91 -18.07,20.43 0,11.26 9.03,20.4 20.24,20.59 3.76,6.52 11.23,10.99 19.88,11.06 8.36,0.06 15.69,-4.01 19.65,-10.13 3.19,3.49 7.78,5.7 12.88,5.7 7.31,0 13.57,-4.5 16.18,-10.88 1.67,0.6 3.47,0.95 5.34,0.95 8.74,0 15.82,-7.08 15.82,-15.82 -0.01,-8.73 -7.09,-15.81 -15.82,-15.81z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="59.36"
android:centerY="-4.66"
android:gradientRadius="110.91"
android:type="radial">
<item android:offset="0.26" android:color="#FFE3F2FD"/>
<item android:offset="0.92" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m51.49,7.62c7.99,0 15.27,4.4 19,11.47 0.42,0.79 1.16,1.35 2.04,1.54 0.2,0.04 0.41,0.06 0.62,0.06 0.67,0 1.33,-0.23 1.87,-0.65 2.65,-2.11 5.84,-3.23 9.22,-3.23 8.18,0 14.84,6.66 14.84,14.84 0,1.42 -0.21,2.84 -0.62,4.23 -0.29,0.97 -0.07,2.01 0.58,2.78 0.57,0.68 1.42,1.07 2.3,1.07 0.12,0 0.23,-0.01 0.35,-0.02 0.56,-0.07 1.05,-0.1 1.5,-0.1 7.07,0 12.82,5.75 12.82,12.82s-5.75,12.82 -12.82,12.82c-1.45,0 -2.91,-0.26 -4.33,-0.77 -0.33,-0.12 -0.68,-0.18 -1.01,-0.18 -1.19,0 -2.3,0.71 -2.78,1.87 -2.23,5.47 -7.49,9.01 -13.4,9.01 -4.04,0 -7.93,-1.72 -10.68,-4.73 -0.57,-0.63 -1.38,-0.98 -2.22,-0.98 -0.08,0 -0.16,0 -0.25,0.01 -0.93,0.08 -1.77,0.77 -2.27,1.55 -3.51,5.42 -9.99,8.97 -16.93,8.97h-0.17c-7.31,0 -13.94,-3.91 -17.3,-9.76 -0.53,-0.91 -1.49,-1.58 -2.55,-1.6 -9.54,-0.16 -17.29,-8.1 -17.29,-17.63 0,-8.87 6.64,-16.4 15.44,-17.48 1.57,-0.19 2.72,-1.59 2.63,-3.17 -0.03,-0.48 -0.04,-0.9 -0.04,-1.29 -0.01,-11.82 9.62,-21.45 21.45,-21.45m0,-3c-13.51,0 -24.46,10.95 -24.46,24.46 0,0.49 0.02,0.98 0.05,1.46 -10.18,1.24 -18.07,9.91 -18.07,20.42 0,11.26 9.03,20.58 20.24,20.77 3.75,6.53 11.23,11.27 19.88,11.27h0.19c8.28,0 15.51,-4.26 19.45,-10.33 3.2,3.5 7.78,5.61 12.89,5.61 7.31,0 13.57,-4.55 16.18,-10.93 1.67,0.6 3.47,0.92 5.34,0.92 8.74,0 15.82,-7.09 15.82,-15.83s-7.08,-15.82 -15.82,-15.82c-0.63,0 -1.24,0.04 -1.85,0.11 0.48,-1.61 0.74,-3.32 0.74,-5.08 0,-9.85 -7.99,-17.84 -17.84,-17.84 -4.19,0 -8.04,1.46 -11.09,3.88 -4.09,-7.77 -12.25,-13.07 -21.65,-13.07z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,92 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m51.04,121.76 l13.8,-24.27c0.39,-0.68 -0.1,-1.53 -0.89,-1.54l-26.19,-0.1c-0.95,0 -1.39,-1.17 -0.68,-1.8l34.24,-30.28c0.85,-0.75 2.12,0.23 1.6,1.24l-10.37,20.07c-0.35,0.68 0.14,1.5 0.91,1.5l26.78,0.17c0.94,0.01 1.38,1.16 0.68,1.79l-38.3,34.49c-0.87,0.79 -2.16,-0.25 -1.58,-1.27z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="59.01"
android:startY="73.87"
android:endX="71.67"
android:endY="118.12"
android:type="linear">
<item android:offset="0" android:color="#FFFBC02D"/>
<item android:offset="1" android:color="#FFFFEB3B"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m65.41,73.01 l-5.52,10.69c-0.65,1.25 -0.6,2.72 0.13,3.93s2.01,1.94 3.42,1.94l21.69,0.14 -25.47,22.93 7.78,-13.68c0.71,-1.24 0.7,-2.78 -0.02,-4.01 -0.71,-1.23 -2.05,-2 -3.47,-2.01l-21.01,-0.08zM71.99,63.51c-0.23,0 -0.46,0.08 -0.67,0.27l-34.24,30.28c-0.71,0.63 -0.27,1.8 0.68,1.8l26.19,0.1c0.79,0 1.28,0.85 0.89,1.54l-13.8,24.27c-0.44,0.77 0.19,1.55 0.9,1.55 0.23,0 0.47,-0.08 0.68,-0.27l38.3,-34.49c0.7,-0.63 0.26,-1.79 -0.68,-1.79l-26.78,-0.17c-0.77,-0.01 -1.26,-0.82 -0.91,-1.5l10.36,-20.06c0.4,-0.79 -0.22,-1.53 -0.92,-1.53z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m20.02,85.12c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28s10.8,-9.36 10.8,-9.36 1.99,11.43 0.55,14.57z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m23.71,107.43c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28 1.44,-3.13 10.8,-9.36 10.8,-9.36s1.99,11.43 0.55,14.57z"
android:fillColor="#90caf9"/>
<path
android:pathData="m44.6,119.67c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28s10.8,-9.36 10.8,-9.36 1.99,11.43 0.55,14.57z"
android:fillColor="#90caf9"/>
<path
android:pathData="m112.91,85.12c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28 1.44,-3.13 10.8,-9.36 10.8,-9.36s1.98,11.43 0.55,14.57z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m88.84,119.67c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28 1.44,-3.13 10.8,-9.36 10.8,-9.36s1.99,11.43 0.55,14.57z"
android:fillColor="#90caf9"/>
<path
android:pathData="m116.01,107.46c-1.44,3.13 -5.14,4.51 -8.28,3.07 -3.13,-1.44 -4.51,-5.14 -3.07,-8.28 1.44,-3.13 10.8,-9.36 10.8,-9.36s1.99,11.44 0.55,14.57z"
android:fillColor="#90caf9"/>
<path
android:pathData="m17.97,74.03c0.58,4.44 0.81,8.97 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -1.03,-0.47 -1.81,-1.32 -2.21,-2.38 -0.39,-1.06 -0.35,-2.22 0.12,-3.25 0.65,-1.41 4.21,-4.33 7.49,-6.71m1.5,-3.47s-9.36,6.22 -10.8,9.36c-1.44,3.13 -0.06,6.84 3.07,8.28 0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.44,-3.15 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m21.66,96.34c0.58,4.44 0.81,8.97 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -2.13,-0.98 -3.06,-3.5 -2.09,-5.63 0.65,-1.41 4.21,-4.33 7.49,-6.71m1.5,-3.48s-9.36,6.22 -10.8,9.36c-1.44,3.13 -0.06,6.84 3.07,8.28 0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.44,-3.14 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m42.56,108.58c0.58,4.44 0.81,8.98 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -1.03,-0.47 -1.81,-1.32 -2.21,-2.38 -0.39,-1.06 -0.35,-2.22 0.12,-3.25 0.65,-1.41 4.2,-4.33 7.49,-6.71m1.49,-3.48s-9.36,6.22 -10.8,9.36 -0.06,6.84 3.07,8.28c0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.44,-3.14 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m110.86,74.03c0.58,4.44 0.81,8.97 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -1.03,-0.47 -1.81,-1.32 -2.21,-2.38 -0.39,-1.06 -0.35,-2.22 0.12,-3.25 0.65,-1.41 4.21,-4.33 7.49,-6.71m1.5,-3.47s-9.36,6.22 -10.8,9.36c-1.44,3.13 -0.06,6.84 3.07,8.28 0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.43,-3.15 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m86.79,108.58c0.58,4.44 0.81,8.97 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -1.03,-0.47 -1.81,-1.32 -2.21,-2.38 -0.39,-1.06 -0.35,-2.22 0.12,-3.25 0.65,-1.41 4.21,-4.33 7.49,-6.71m1.5,-3.48s-9.36,6.22 -10.8,9.36c-1.44,3.13 -0.06,6.84 3.07,8.28 0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.44,-3.14 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m113.96,96.37c0.58,4.44 0.81,8.98 0.23,10.25 -0.69,1.5 -2.21,2.48 -3.86,2.48 -0.61,0 -1.2,-0.13 -1.77,-0.39 -2.13,-0.98 -3.06,-3.5 -2.09,-5.63 0.65,-1.4 4.21,-4.33 7.49,-6.71m1.5,-3.47s-9.36,6.22 -10.8,9.36c-1.44,3.13 -0.06,6.84 3.07,8.28 0.84,0.39 1.73,0.57 2.6,0.57 2.37,0 4.63,-1.35 5.68,-3.64 1.44,-3.14 -0.55,-14.57 -0.55,-14.57z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m103.18,36.62c-0.63,0 -1.24,0.05 -1.85,0.12 0.48,-1.61 0.74,-3.32 0.74,-5.08 0,-9.85 -7.99,-17.84 -17.84,-17.84 -4.19,0 -8.04,1.46 -11.09,3.88 -4.09,-7.78 -12.25,-13.08 -21.65,-13.08 -13.51,0 -24.46,10.95 -24.46,24.46 0,0.49 0.02,0.97 0.05,1.45 -10.19,1.24 -18.07,9.91 -18.07,20.43 0,11.26 9.03,20.4 20.24,20.59 3.76,6.52 11.23,10.99 19.88,11.06 8.36,0.06 15.69,-4.01 19.65,-10.13 3.19,3.49 7.78,5.7 12.88,5.7 7.31,0 13.57,-4.5 16.18,-10.88 1.67,0.6 3.47,0.95 5.34,0.95 8.74,0 15.82,-7.08 15.82,-15.82 -0.01,-8.73 -7.09,-15.81 -15.82,-15.81z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="59.36"
android:centerY="-4.66"
android:gradientRadius="110.91"
android:type="radial">
<item android:offset="0.26" android:color="#FFE3F2FD"/>
<item android:offset="0.92" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m51.49,7.62c7.99,0 15.27,4.4 19,11.47 0.42,0.79 1.16,1.35 2.04,1.54 0.2,0.04 0.41,0.06 0.62,0.06 0.67,0 1.33,-0.23 1.87,-0.65 2.65,-2.11 5.84,-3.23 9.22,-3.23 8.18,0 14.84,6.66 14.84,14.84 0,1.42 -0.21,2.84 -0.62,4.23 -0.29,0.97 -0.07,2.01 0.58,2.78 0.57,0.68 1.42,1.07 2.3,1.07 0.12,0 0.23,-0.01 0.35,-0.02 0.56,-0.07 1.05,-0.1 1.5,-0.1 7.07,0 12.82,5.75 12.82,12.82s-5.75,12.82 -12.82,12.82c-1.45,0 -2.91,-0.26 -4.33,-0.77 -0.33,-0.12 -0.68,-0.18 -1.01,-0.18 -1.19,0 -2.3,0.71 -2.78,1.87 -2.23,5.47 -7.49,9.01 -13.4,9.01 -4.04,0 -7.93,-1.72 -10.68,-4.73 -0.57,-0.63 -1.38,-0.98 -2.22,-0.98 -0.08,0 -0.16,0 -0.25,0.01 -0.93,0.08 -1.77,0.77 -2.27,1.55 -3.51,5.42 -9.99,8.97 -16.93,8.97h-0.17c-7.31,0 -13.94,-3.91 -17.3,-9.76 -0.53,-0.91 -1.49,-1.58 -2.55,-1.6 -9.54,-0.16 -17.29,-8.1 -17.29,-17.63 0,-8.87 6.64,-16.4 15.44,-17.48 1.57,-0.19 2.72,-1.59 2.63,-3.17 -0.03,-0.48 -0.04,-0.9 -0.04,-1.29 -0.01,-11.82 9.62,-21.45 21.45,-21.45m0,-3c-13.51,0 -24.46,10.95 -24.46,24.46 0,0.49 0.02,0.98 0.05,1.46 -10.18,1.24 -18.07,9.91 -18.07,20.42 0,11.26 9.03,20.58 20.24,20.77 3.75,6.53 11.23,11.27 19.88,11.27h0.19c8.28,0 15.51,-4.26 19.45,-10.33 3.2,3.5 7.78,5.61 12.89,5.61 7.31,0 13.57,-4.55 16.18,-10.93 1.67,0.6 3.47,0.92 5.34,0.92 8.74,0 15.82,-7.09 15.82,-15.83s-7.08,-15.82 -15.82,-15.82c-0.63,0 -1.24,0.04 -1.85,0.11 0.48,-1.61 0.74,-3.32 0.74,-5.08 0,-9.85 -7.99,-17.84 -17.84,-17.84 -4.19,0 -8.04,1.46 -11.09,3.88 -4.09,-7.77 -12.25,-13.07 -21.65,-13.07z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,73 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m106.97,39.56c-0.68,0 -1.35,0.05 -2,0.13 0.52,-1.75 0.81,-3.59 0.81,-5.51 0,-10.68 -8.66,-19.34 -19.34,-19.34 -4.55,0 -8.72,1.58 -12.02,4.21 -4.45,-8.43 -13.29,-14.18 -23.48,-14.18 -14.64,0 -26.52,11.87 -26.52,26.52 0,0.53 0.02,1.05 0.05,1.57 -11.03,1.35 -19.58,10.75 -19.58,22.15 0,12.2 9.79,22.11 21.94,22.32 4.07,7.07 12.18,11.92 21.55,11.99 9.06,0.07 17,-4.35 21.3,-10.98 3.46,3.79 8.43,6.18 13.96,6.18 7.93,0 14.71,-4.88 17.53,-11.79 1.81,0.65 3.76,1.02 5.79,1.02 9.47,0 17.15,-7.68 17.15,-17.15 0,-9.46 -7.68,-17.14 -17.14,-17.14z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="59.47"
android:centerY="-5.18"
android:gradientRadius="120.22"
android:type="radial">
<item android:offset="0.26" android:color="#FFE3F2FD"/>
<item android:offset="0.92" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m50.94,7.87c8.75,0 16.73,4.82 20.81,12.57 0.42,0.79 1.16,1.35 2.04,1.54 0.2,0.04 0.41,0.06 0.62,0.06 0.67,0 1.33,-0.23 1.87,-0.65 2.92,-2.32 6.43,-3.55 10.15,-3.55 9.01,0 16.34,7.33 16.34,16.34 0,1.56 -0.23,3.13 -0.68,4.66 -0.29,0.97 -0.07,2.01 0.58,2.78 0.57,0.68 1.42,1.07 2.3,1.07 0.12,0 0.23,-0.01 0.35,-0.02 0.62,-0.07 1.16,-0.11 1.66,-0.11 7.8,0 14.15,6.35 14.15,14.15s-6.35,14.15 -14.15,14.15c-1.61,0 -3.21,-0.29 -4.78,-0.85 -0.33,-0.12 -0.68,-0.18 -1.01,-0.18 -1.19,0 -2.3,0.71 -2.78,1.87 -2.46,6.03 -8.25,9.92 -14.76,9.92 -4.45,0 -8.74,-1.9 -11.76,-5.21 -0.57,-0.63 -1.38,-0.98 -2.22,-0.98 -0.08,0 -0.16,0 -0.25,0.01 -0.93,0.08 -1.77,0.37 -2.27,1.15 -3.85,5.93 -10.96,9.41 -18.56,9.41h-0.19c-8.01,0 -15.28,-3.87 -18.97,-10.28 -0.53,-0.91 -1.49,-1.38 -2.55,-1.4 -10.47,-0.18 -18.99,-8.79 -18.99,-19.26 0,-9.74 7.29,-17.95 16.95,-19.14 1.57,-0.19 2.72,-1.56 2.63,-3.14 -0.03,-0.52 -0.05,-0.98 -0.05,-1.4 0.01,-12.96 10.56,-23.51 23.52,-23.51m0,-3c-14.64,0 -26.52,11.87 -26.52,26.52 0,0.53 0.02,1.06 0.05,1.58 -11.03,1.35 -19.58,10.74 -19.58,22.14 0,12.2 9.79,21.89 21.94,22.1 4.07,7.07 12.17,11.79 21.55,11.79h0.21c8.98,0 16.82,-4.2 21.08,-10.78 3.46,3.79 8.43,6.29 13.98,6.29 7.93,0 14.71,-4.82 17.53,-11.74 1.81,0.65 3.76,1.05 5.79,1.05 9.47,0 17.15,-7.66 17.15,-17.13s-7.68,-17.14 -17.15,-17.14c-0.68,0 -1.35,0.05 -2,0.13 0.52,-1.75 0.81,-3.59 0.81,-5.51 0,-10.68 -8.66,-19.34 -19.34,-19.34 -4.55,0 -8.72,1.58 -12.02,4.21 -4.45,-8.42 -13.29,-14.17 -23.48,-14.17z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m97.36,119.15c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m97.36,119.15c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#90caf9"/>
<path
android:pathData="m25.59,96.63c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m23.42,82.08c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -1.39,-0.64 -2.45,-1.78 -2.98,-3.21s-0.47,-2.99 0.16,-4.38c0.89,-1.91 5.79,-5.9 10.06,-8.95m1.49,-3.46s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m39.66,119.15c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#90caf9"/>
<path
android:pathData="m37.5,104.59c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -2.87,-1.32 -4.13,-4.72 -2.82,-7.59 0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m69.22,107.89c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m67.05,93.33c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -2.87,-1.32 -4.13,-4.72 -2.82,-7.59 0.88,-1.91 5.78,-5.89 10.06,-8.95m1.49,-3.45s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.22,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.78,-3.89 -0.68,-18.02 -0.68,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m115.66,96.63c-1.78,3.88 -6.36,5.58 -10.24,3.8s-5.58,-6.36 -3.8,-10.24 13.36,-11.57 13.36,-11.57 2.46,14.13 0.68,18.01z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m113.49,82.08c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -2.87,-1.32 -4.13,-4.72 -2.82,-7.59 0.88,-1.91 5.78,-5.9 10.06,-8.95m1.49,-3.46s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m95.2,104.59c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -1.39,-0.64 -2.45,-1.78 -2.98,-3.21s-0.47,-2.99 0.16,-4.38c0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m95.2,104.59c0.74,5.36 1.22,11.83 0.35,13.72 -0.93,2.03 -2.97,3.34 -5.21,3.34 -0.82,0 -1.62,-0.18 -2.38,-0.52 -1.39,-0.64 -2.45,-1.78 -2.98,-3.21s-0.47,-2.99 0.16,-4.38c0.88,-1.91 5.78,-5.89 10.06,-8.95m1.48,-3.45s-11.58,7.7 -13.36,11.57c-1.78,3.88 -0.08,8.46 3.8,10.24 1.04,0.48 2.14,0.71 3.21,0.71 2.93,0 5.72,-1.67 7.02,-4.5 1.79,-3.89 -0.67,-18.02 -0.67,-18.02z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,55 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m106.97,37.87c-0.68,0 -1.35,0.05 -2,0.12 0.52,-1.71 0.81,-3.51 0.81,-5.38 0,-10.43 -8.66,-18.88 -19.34,-18.88 -4.55,0 -8.72,1.54 -12.02,4.11 -4.45,-8.23 -13.29,-13.84 -23.48,-13.84 -14.64,0 -26.51,11.59 -26.51,25.89 0,0.51 0.02,1.02 0.05,1.53 -11.04,1.32 -19.59,10.5 -19.59,21.63 0,11.91 9.79,21.59 21.94,21.79 4.07,6.9 12.18,11.63 21.55,11.7 9.06,0.07 17,-4.25 21.3,-10.72 3.46,3.7 8.43,6.03 13.96,6.03 7.93,0 14.71,-4.76 17.53,-11.51 1.81,0.64 3.76,1 5.79,1 9.47,0 17.15,-7.49 17.15,-16.74 0,-9.24 -7.68,-16.73 -17.14,-16.73z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="59.47"
android:centerY="-5.81"
android:gradientRadius="120.22"
android:type="radial">
<item android:offset="0.26" android:color="#FFE3F2FD"/>
<item android:offset="0.92" android:color="#FF90CAF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m50.94,7c8.76,0 16.74,4.7 20.83,12.26 0.42,0.78 1.16,1.33 2.02,1.51 0.21,0.04 0.41,0.06 0.62,0.06 0.66,0 1.31,-0.22 1.84,-0.63 2.93,-2.27 6.45,-3.48 10.18,-3.48 9.01,0 16.34,7.12 16.34,15.88 0,1.51 -0.23,3.03 -0.68,4.51 -0.29,0.97 -0.08,2.02 0.56,2.79 0.57,0.69 1.42,1.08 2.3,1.08 0.11,0 0.23,-0.01 0.34,-0.02 0.62,-0.07 1.16,-0.1 1.66,-0.1 7.8,0 14.15,6.16 14.15,13.74s-6.35,13.74 -14.15,13.74c-1.61,0 -3.23,-0.28 -4.8,-0.83 -0.33,-0.12 -0.66,-0.17 -0.99,-0.17 -1.18,0 -2.29,0.7 -2.77,1.84 -2.46,5.87 -8.25,9.67 -14.77,9.67 -4.46,0 -8.76,-1.86 -11.78,-5.09 -0.57,-0.61 -1.36,-0.95 -2.19,-0.95 -0.08,0 -0.17,0 -0.25,0.01 -0.91,0.08 -1.74,0.79 -2.25,1.56 -3.83,5.8 -10.95,9.62 -18.56,9.62h-0.19c-8.02,-1 -15.3,-4.21 -18.99,-10.46 -0.53,-0.9 -1.49,-1.57 -2.53,-1.59 -10.47,-0.18 -18.99,-8.66 -18.99,-18.84 0,-9.47 7.29,-17.51 16.95,-18.67 1.58,-0.19 2.73,-1.59 2.64,-3.17 -0.03,-0.51 -0.05,-0.96 -0.05,-1.37 0,-12.63 10.55,-22.9 23.51,-22.9m0,-3c-14.64,0 -26.51,11.59 -26.51,25.89 0,0.52 0.02,1.03 0.05,1.54 -11.04,1.32 -19.59,10.49 -19.59,21.62 0,11.91 9.79,21.8 21.94,22.01 4.07,6.9 12.17,10.94 21.55,11.94h0.21c8.98,0 16.82,-4.53 21.08,-10.96 3.46,3.7 8.43,5.93 13.98,5.93 7.93,0 14.71,-4.82 17.53,-11.57 1.81,0.64 3.76,0.97 5.79,0.97 9.47,0 17.15,-7.51 17.15,-16.75s-7.68,-16.75 -17.15,-16.75c-0.68,0 -1.35,0.05 -2,0.12 0.52,-1.71 0.81,-3.51 0.81,-5.38 0,-10.43 -8.66,-18.88 -19.34,-18.88 -4.55,0 -8.72,1.54 -12.02,4.11 -4.45,-8.23 -13.29,-13.84 -23.48,-13.84z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
<path
android:pathData="m21.61,104.5 l-0.63,0.02c-0.83,0.03 -1.52,-0.62 -1.55,-1.45l-0.67,-21.17c-0.03,-0.83 0.62,-1.52 1.45,-1.55l0.63,-0.02c0.83,-0.03 1.52,0.62 1.55,1.45l0.68,21.17c0.02,0.83 -0.63,1.52 -1.46,1.55z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m30.95,95.56 l-6.15,-3.26 5.93,-3.64c0.87,-0.53 1.17,-1.73 0.67,-2.67s-1.61,-1.28 -2.48,-0.74l-8.09,4.97 -8.39,-4.44c-0.9,-0.48 -1.99,-0.07 -2.42,0.9 -0.44,0.97 -0.06,2.15 0.84,2.63l6.16,3.25 -5.93,3.64c-0.87,0.53 -1.17,1.73 -0.67,2.67s1.61,1.28 2.48,0.74l8.09,-4.97 8.39,4.44c0.9,0.48 1.99,0.07 2.42,-0.9s0.05,-2.15 -0.85,-2.62z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m97.88,82.66 l0.36,-0.51c0.48,-0.67 1.42,-0.83 2.09,-0.35l17.25,12.29c0.67,0.48 0.83,1.42 0.35,2.09l-0.36,0.51c-0.48,0.67 -1.42,0.83 -2.09,0.35l-17.25,-12.28c-0.67,-0.49 -0.83,-1.42 -0.35,-2.1z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m99.33,95.51 l6.32,-2.92 -0.7,6.93c-0.1,1.01 0.67,1.98 1.72,2.16s1.99,-0.5 2.09,-1.52l0.95,-9.44 8.62,-3.98c0.93,-0.43 1.26,-1.53 0.75,-2.47s-1.67,-1.35 -2.6,-0.92l-6.32,2.92 0.7,-6.93c0.1,-1.02 -0.67,-1.98 -1.72,-2.16s-1.99,0.5 -2.09,1.52l-0.95,9.44 -8.62,3.98c-0.93,0.43 -1.26,1.53 -0.75,2.47s1.68,1.35 2.6,0.92z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m62.17,112.31 l-0.61,-0.13c-0.81,-0.17 -1.33,-0.97 -1.15,-1.78l4.46,-20.71c0.17,-0.81 0.97,-1.33 1.78,-1.15l0.61,0.13c0.81,0.17 1.33,0.97 1.15,1.78l-4.46,20.71c-0.17,0.81 -0.97,1.33 -1.78,1.15z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m73.4,105.89 l-5.19,-4.65 6.64,-2.1c0.97,-0.31 1.55,-1.4 1.3,-2.43 -0.26,-1.04 -1.25,-1.63 -2.22,-1.32l-9.05,2.86 -7.07,-6.33c-0.76,-0.68 -1.91,-0.55 -2.57,0.29s-0.58,2.07 0.18,2.75l5.19,4.65 -6.64,2.1c-0.97,0.31 -1.55,1.4 -1.3,2.43 0.25,1.04 1.25,1.63 2.22,1.32l9.05,-2.86 7.07,6.33c0.76,0.68 1.91,0.55 2.57,-0.29s0.58,-2.07 -0.18,-2.75z"
android:fillColor="#64b5f6"/>
<path
android:pathData="m32.23,117.52c-0.45,-0.45 -0.45,-1.18 0,-1.63l9.23,-9.23c0.45,-0.45 1.18,-0.45 1.63,0s0.45,1.18 0,1.63l-9.23,9.23c-0.45,0.45 -1.18,0.45 -1.63,0z"
android:fillColor="#90caf9"/>
<path
android:pathData="m40.57,118.1 l-1.17,-4.27 4.27,1.17c0.62,0.17 1.31,-0.21 1.53,-0.85s-0.11,-1.3 -0.73,-1.47l-5.82,-1.59 -1.59,-5.82c-0.17,-0.62 -0.83,-0.95 -1.47,-0.73s-1.02,0.91 -0.85,1.53l1.17,4.27 -4.27,-1.17c-0.62,-0.17 -1.31,0.21 -1.53,0.85s0.11,1.3 0.73,1.47l5.82,1.59 1.59,5.82c0.17,0.62 0.83,0.95 1.47,0.73s1.02,-0.9 0.85,-1.53z"
android:fillColor="#90caf9"/>
<path
android:pathData="m88.94,117.06c-0.58,-0.27 -0.83,-0.95 -0.56,-1.53l5.48,-11.85c0.27,-0.58 0.95,-0.83 1.53,-0.56s0.83,0.95 0.56,1.53l-5.48,11.85c-0.27,0.58 -0.95,0.83 -1.53,0.56z"
android:fillColor="#90caf9"/>
<path
android:pathData="m96.97,114.73 l-2.57,-3.6 4.41,-0.38c0.65,-0.06 1.16,-0.65 1.14,-1.33 -0.01,-0.68 -0.55,-1.18 -1.19,-1.13l-6.01,0.52 -3.5,-4.91c-0.38,-0.53 -1.11,-0.61 -1.63,-0.18s-0.65,1.2 -0.27,1.73l2.57,3.6 -4.41,0.38c-0.65,0.06 -1.16,0.65 -1.14,1.33 0.01,0.68 0.55,1.18 1.19,1.13l6.01,-0.52 3.5,4.91c0.38,0.53 1.11,0.61 1.63,0.18s0.64,-1.21 0.27,-1.73z"
android:fillColor="#90caf9"/>
</vector>

View File

@ -1,84 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m89.47,28.93c-2.46,-9.66 -7.29,-18.09 -13.69,-24.84 20.75,5.42 38.01,21.66 43.67,43.91 8.29,32.59 -11.41,65.72 -43.99,74.01 -27.79,7.07 -55.96,-6.22 -68.8,-30.49 12.29,7.12 27.26,9.64 42.12,5.86 30.14,-7.65 48.36,-38.31 40.69,-68.45z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="67.56"
android:centerY="24.47"
android:gradientRadius="96.59"
android:type="radial">
<item android:offset="0.28" android:color="#FFFFF157"/>
<item android:offset="0.52" android:color="#FFFEEE54"/>
<item android:offset="0.72" android:color="#FFFAE44A"/>
<item android:offset="0.9" android:color="#FFF4D538"/>
<item android:offset="1" android:color="#FFF0C92C"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m91.78,80.3c0.61,6.13 6.41,10.57 12.96,9.92 6.54,-0.65 11.35,-6.15 10.74,-12.28s-6.41,-10.57 -12.96,-9.92c-6.54,0.65 -11.35,6.15 -10.74,12.28z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="103.31"
android:centerY="75.44"
android:gradientRadius="14.31"
android:type="radial">
<item android:offset="0.01" android:color="#FFE0A800"/>
<item android:offset="0.61" android:color="#1BE0A800"/>
<item android:offset="0.68" android:color="#00E0A800"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m89.42,103.89c1.77,3.82 6.29,5.48 10.09,3.72 3.8,-1.77 5.44,-6.29 3.67,-10.11s-6.29,-5.48 -10.09,-3.72c-3.8,1.77 -5.44,6.3 -3.67,10.11z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="95.12"
android:centerY="92.32"
android:gradientRadius="13.45"
android:type="radial">
<item android:offset="0.01" android:color="#FFE0A800"/>
<item android:offset="0.61" android:color="#1BE0A800"/>
<item android:offset="0.68" android:color="#00E0A800"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m107.44,62.08c1.21,2.61 4.31,3.75 6.91,2.54s3.73,-4.31 2.51,-6.92c-1.21,-2.61 -4.31,-3.75 -6.91,-2.54 -2.6,1.2 -3.73,4.3 -2.51,6.92z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="111.34"
android:centerY="54.15"
android:gradientRadius="9.2"
android:type="radial">
<item android:offset="0.01" android:color="#FFE0A800"/>
<item android:offset="0.61" android:color="#1BE0A800"/>
<item android:offset="0.68" android:color="#00E0A800"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M73.24,109.06m-8.97,0a8.97,8.97 0,1 1,17.94 0a8.97,8.97 0,1 1,-17.94 0">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="73.01"
android:centerY="105.52"
android:gradientRadius="15.24"
android:type="radial">
<item android:offset="0.01" android:color="#FFE0A800"/>
<item android:offset="0.61" android:color="#1BE0A800"/>
<item android:offset="0.68" android:color="#00E0A800"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m84.57,10.4c15.75,7.23 27.62,21.23 31.98,38.34 7.87,30.93 -10.89,62.5 -41.83,70.37 -4.7,1.19 -9.51,1.8 -14.31,1.8 -18.06,0 -34.78,-8.35 -45.62,-22.3 6.43,2.32 13.23,3.53 20.08,3.53 4.91,0 9.84,-0.62 14.65,-1.84 31.7,-8.06 50.93,-40.41 42.86,-72.11 -1.61,-6.33 -4.27,-12.35 -7.81,-17.79m-8.79,-6.31c6.41,6.75 11.23,15.18 13.69,24.84 7.67,30.14 -10.55,60.79 -40.69,68.46 -4.65,1.18 -9.32,1.75 -13.91,1.75 -10.06,0 -19.77,-2.72 -28.21,-7.61 10.51,19.87 31.31,32.38 53.75,32.38 4.97,0 10.01,-0.61 15.05,-1.89 32.59,-8.29 52.28,-41.43 43.99,-74.01 -5.66,-22.26 -22.92,-38.5 -43.67,-43.92z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

View File

@ -1,23 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m64.6,124c-20.1,0 -35.56,-19.48 -35.56,-36.18 0,-11.75 5.26,-25.36 12.68,-44.22 0.93,-2.78 2.16,-5.57 3.4,-8.66 3.54,-8.84 6.66,-18.42 11.41,-26.71 3.24,-5.66 11.46,-5.65 14.58,0.08 4.43,8.14 7.45,16.96 12.05,27.25 12.99,29.07 16.7,40.82 16.7,52.57 0.3,16.39 -15.47,35.87 -35.26,35.87z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="67.14"
android:centerY="19.26"
android:gradientRadius="79.34"
android:type="radial">
<item android:offset="0.46" android:color="#FF29B6F6"/>
<item android:offset="1" android:color="#FF1E88E5"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m86.34,101.71c-4.55,7.02 -14.84,5.69 -14.84,-5.97 0,-7.45 1.52,-45.74 7.92,-40.39 10.41,8.72 13.38,36.43 6.92,46.36z"
android:fillColor="#81d4fa"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -1,43 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m81.56,35.12 l23.3,-13.38c1.48,-0.85 3.12,0.83 2.24,2.29l-13.86,23.01c-2.07,3.44 -0.28,7.91 3.6,8.95l25.94,7.01c1.65,0.45 1.62,2.79 -0.03,3.2l-26.08,6.47c-3.9,0.97 -5.79,5.4 -3.79,8.88l13.38,23.3c0.85,1.48 -0.83,3.12 -2.29,2.24l-23.01,-13.85c-3.44,-2.07 -7.91,-0.28 -8.95,3.6l-7.01,25.94c-0.45,1.65 -2.79,1.62 -3.2,-0.03l-6.47,-26.08c-0.97,-3.9 -5.4,-5.79 -8.88,-3.79l-23.3,13.38c-1.48,0.85 -3.12,-0.83 -2.24,-2.29l13.86,-23.02c2.07,-3.44 0.28,-7.91 -3.6,-8.95l-25.95,-7.01c-1.65,-0.45 -1.62,-2.79 0.03,-3.2l26.08,-6.47c3.9,-0.97 5.79,-5.4 3.79,-8.88l-13.38,-23.3c-0.85,-1.48 0.83,-3.12 2.29,-2.24l23.02,13.86c3.44,2.07 7.91,0.28 8.95,-3.6l7.01,-25.94c0.45,-1.65 2.79,-1.62 3.2,0.03l6.47,26.08c0.97,3.9 5.4,5.79 8.88,3.79z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="64.01"
android:centerY="64"
android:gradientRadius="55.57"
android:type="radial">
<item android:offset="0.39" android:color="#FFFF8F00"/>
<item android:offset="0.82" android:color="#FFFFB300"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M64.07,63.97m-30.03,0a30.03,30.03 0,1 1,60.06 0a30.03,30.03 0,1 1,-60.06 0">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="64.07"
android:centerY="63.97"
android:gradientRadius="37.05"
android:type="radial">
<item android:offset="0.58" android:color="#FFFFEB3B"/>
<item android:offset="0.84" android:color="#FFFBC02D"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m64.11,35.99c3.74,0 7.4,0.74 10.87,2.21 14.21,6.01 20.88,22.47 14.87,36.68 -4.39,10.38 -14.52,17.08 -25.81,17.08 -3.74,0 -7.4,-0.74 -10.87,-2.21 -14.21,-6.02 -20.88,-22.47 -14.87,-36.69 4.39,-10.37 14.52,-17.07 25.81,-17.07m0,-2c-11.69,0 -22.82,6.88 -27.66,18.3 -6.44,15.23 0.7,32.86 15.93,39.3 3.8,1.61 7.75,2.37 11.65,2.37 11.69,0 22.82,-6.88 27.66,-18.3 6.44,-15.23 -0.7,-32.86 -15.93,-39.3 -3.8,-1.61 -7.76,-2.37 -11.65,-2.37z"
android:strokeAlpha="0.2"
android:fillColor="#eee"
android:fillAlpha="0.2"/>
<path
android:pathData="m64.55,11.02 l5.22,21.04c1,4.04 4.62,6.87 8.79,6.87 1.57,0 3.12,-0.42 4.5,-1.21l18.8,-10.8 -11.19,18.57c-1.48,2.45 -1.71,5.38 -0.64,8.04s3.27,4.61 6.03,5.36l20.92,5.66 -21.04,5.22c-2.78,0.69 -5.02,2.6 -6.14,5.23s-0.95,5.57 0.48,8.05l10.8,18.8 -18.57,-11.18c-1.41,-0.85 -3.02,-1.3 -4.66,-1.3 -4.08,0 -7.68,2.75 -8.74,6.69l-5.66,20.92 -5.22,-21.04c-1,-4.04 -4.62,-6.87 -8.79,-6.87 -1.57,0 -3.12,0.42 -4.5,1.21l-18.8,10.8 11.18,-18.57c1.48,-2.45 1.71,-5.38 0.64,-8.04s-3.27,-4.61 -6.03,-5.36l-20.92,-5.66 21.04,-5.22c2.78,-0.69 5.02,-2.6 6.14,-5.23s0.95,-5.57 -0.48,-8.05l-10.8,-18.8 18.57,11.18c1.41,0.85 3.02,1.3 4.66,1.3 4.08,0 7.68,-2.75 8.74,-6.69zM64.61,4c-0.69,0 -1.37,0.41 -1.59,1.22l-7.02,25.94c-0.75,2.76 -3.23,4.47 -5.85,4.47 -1.05,0 -2.12,-0.28 -3.11,-0.87l-23.02,-13.86c-0.28,-0.17 -0.58,-0.25 -0.86,-0.25 -1.16,0 -2.11,1.3 -1.43,2.49l13.38,23.3c2,3.48 0.11,7.91 -3.79,8.88l-26.07,6.47c-1.66,0.41 -1.68,2.75 -0.03,3.2l25.94,7.01c3.88,1.05 5.67,5.51 3.6,8.95l-13.86,23.03c-0.72,1.19 0.23,2.52 1.41,2.52 0.27,0 0.55,-0.07 0.83,-0.23l23.3,-13.38c0.96,-0.55 1.99,-0.81 3,-0.81 2.66,0 5.18,1.77 5.88,4.59l6.47,26.08c0.21,0.83 0.91,1.25 1.6,1.25s1.37,-0.41 1.59,-1.22l7.02,-25.94c0.75,-2.76 3.23,-4.47 5.85,-4.47 1.05,0 2.12,0.28 3.11,0.87l23.02,13.86c0.28,0.17 0.58,0.25 0.86,0.25 1.16,0 2.11,-1.3 1.43,-2.49l-13.38,-23.3c-2,-3.48 -0.11,-7.91 3.79,-8.88l26.08,-6.47c1.66,-0.41 1.68,-2.75 0.03,-3.2l-25.95,-7.01c-3.88,-1.05 -5.67,-5.51 -3.6,-8.95l13.86,-23.02c0.72,-1.19 -0.23,-2.52 -1.41,-2.52 -0.27,0 -0.55,0.07 -0.83,0.23l-23.3,13.38c-0.96,0.55 -1.99,0.81 -3,0.81 -2.66,0 -5.18,-1.77 -5.88,-4.59l-6.47,-26.09c-0.21,-0.83 -0.91,-1.25 -1.6,-1.25z"
android:strokeAlpha="0.2"
android:fillColor="#eee"
android:fillAlpha="0.2"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -1,38 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="M76,80.34V16.29C76,9.77 70.82,4 64.3,4h-1.55C56.24,4 51,9.77 51,16.29v64.04c-7,4.2 -11.24,11.68 -11.24,20.2c0,13.14 10.64,23.8 23.79,23.8s23.74,-10.65 23.74,-23.8C87.28,92.01 83,84.54 76,80.34z"
android:fillColor="#E1F5FE"/>
<path
android:pathData="m69.92,85.89v-62.07c0,-3.53 -2.86,-6.39 -6.39,-6.39s-6.39,2.86 -6.39,6.39v62.08c-5.82,2.54 -9.83,8.44 -9.58,15.25 0.3,8.19 6.99,14.96 15.18,15.36 9.19,0.44 16.78,-6.87 16.78,-15.96 -0.01,-6.57 -3.95,-12.19 -9.6,-14.66z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="67.01"
android:startY="38.59"
android:endX="63.28"
android:endY="104.92"
android:type="linear">
<item android:offset="0.6" android:color="#FFEF5350"/>
<item android:offset="1" android:color="#FFE53935"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m62.87,39.76h-11.92v-6.39h11.92c1.66,0 3,1.34 3,3v0.39c0,1.66 -1.34,3 -3,3z"
android:fillColor="#616161"/>
<path
android:pathData="m62.87,58.32h-11.92v-6.39h11.92c1.66,0 3,1.34 3,3v0.39c0,1.65 -1.34,3 -3,3z"
android:fillColor="#616161"/>
<path
android:pathData="m62.87,76.88h-11.92v-6.39h11.92c1.66,0 3,1.34 3,3v0.39c0,1.65 -1.34,3 -3,3z"
android:fillColor="#616161"/>
<path
android:pathData="M64.15,7C68.87,7 73,11.26 73,16.29v64.04c0,1.05 0.55,2.03 1.46,2.57c6.15,3.69 9.82,10.28 9.82,17.63c0,11.47 -9.3,20.8 -20.74,20.8c-11.46,0 -20.79,-9.33 -20.79,-20.8c0,-7.36 3.66,-13.95 9.79,-17.63c0.9,-0.54 1.46,-1.52 1.46,-2.57V16.29C54,11.26 58.01,7 62.76,7H64M64.3,4h-1.55C56.24,4 51,9.77 51,16.29v64.04c-7,4.2 -11.24,11.68 -11.24,20.2c0,13.14 10.64,23.8 23.79,23.8s23.74,-10.65 23.74,-23.8c0,-8.52 -4.28,-16 -11.28,-20.2V16.29C76,9.77 70.82,4 64.3,4L64.3,4z"
android:strokeAlpha="0.2"
android:fillColor="#424242"
android:fillAlpha="0.2"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -3,4 +3,6 @@ plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false
alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.google.gms.google.services) apply false
alias(libs.plugins.google.firebase.crashlytics) apply false
} }

View File

@ -11,15 +11,20 @@ androidxComposeMaterial = "1.3.1"
glance = "1.1.1" glance = "1.1.1"
kotlinxSerializationJson = "1.8.0" kotlinxSerializationJson = "1.8.0"
mapboxSdkTurf = "7.3.1" mapboxSdkTurf = "7.3.1"
firebaseCrashlytics = "19.4.2"
googleGmsGoogleServices = "4.4.2"
googleFirebaseCrashlytics = "3.0.3"
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" }
google-firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "googleFirebaseCrashlytics" }
[libraries] [libraries]
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
hammerhead-karoo-ext = { group = "io.hammerhead", name = "karoo-ext", version = "1.1.5" } hammerhead-karoo-ext = { group = "io.hammerhead", name = "karoo-ext", version = "1.1.3" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }
# compose # compose
@ -38,6 +43,7 @@ androidx-glance-appwidget-preview = { group = "androidx.glance", name = "glance-
androidx-glance-preview = { group = "androidx.glance", name = "glance-preview", version.ref = "glance" } androidx-glance-preview = { group = "androidx.glance", name = "glance-preview", version.ref = "glance" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
mapbox-sdk-turf = { module = "com.mapbox.mapboxsdk:mapbox-sdk-turf", version.ref = "mapboxSdkTurf" } mapbox-sdk-turf = { module = "com.mapbox.mapboxsdk:mapbox-sdk-turf", version.ref = "mapboxSdkTurf" }
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version.ref = "firebaseCrashlytics" }
[bundles] [bundles]
androidx-lifeycle = ["androidx-lifecycle-runtime-compose", "androidx-lifecycle-viewmodel-compose"] androidx-lifeycle = ["androidx-lifecycle-runtime-compose", "androidx-lifecycle-viewmodel-compose"]