Add pull-down-to-refresh on main screen (#57)
* Add pull-down-to-refresh on main screen * Update README with screenshots
This commit is contained in:
parent
7c28251b02
commit
4cd6d27aa2
10
README.md
10
README.md
@ -10,9 +10,10 @@ Compatible with Karoo 2 and Karoo 3 devices.
|
|||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/timklge" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
|
<a href="https://www.buymeacoffee.com/timklge" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -37,7 +38,8 @@ After installing this app on your Karoo and opening it once from the main menu,
|
|||||||
|
|
||||||
- Headwind (graphical, 1x1 field): Shows the headwind direction and speed as a circle with a triangular direction indicator. The speed is shown at the center in your set unit of measurement (default is kilometers per hour if you have set up metric units in your Karoo, otherwise miles per hour). Both direction and speed are relative to the current riding direction by default, i. e., riding directly into a wind of 20 km/h will show a headwind speed of 20 km/h, while riding in the same direction will show -20 km/h. You can change this behavior in the app settings to show the absolute wind direction and speed instead.
|
- Headwind (graphical, 1x1 field): Shows the headwind direction and speed as a circle with a triangular direction indicator. The speed is shown at the center in your set unit of measurement (default is kilometers per hour if you have set up metric units in your Karoo, otherwise miles per hour). Both direction and speed are relative to the current riding direction by default, i. e., riding directly into a wind of 20 km/h will show a headwind speed of 20 km/h, while riding in the same direction will show -20 km/h. You can change this behavior in the app settings to show the absolute wind direction and speed instead.
|
||||||
- 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.
|
||||||
- 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.
|
- 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). Tap on this widget to open the headwind app with a forecast overview.
|
||||||
- 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.
|
||||||
|
|
||||||
The app will automatically attempt to download weather data for your current approximate location from the [open-meteo.com](https://open-meteo.com) API once your device has acquired a GPS fix. The API service is free for non-commercial use. Your location is rounded to approximately two kilometers to maintain privacy. The data is updated when you ride more than two kilometers from the location where the weather data was downloaded or after one hour at the latest. If the app cannot connect to the weather service, it will retry the download every minute. Downloading weather data should work on Karoo 2 if you have a SIM card inserted or on Karoo 3 via your phone's internet connection if you have the Karoo companion app installed.
|
The app will automatically attempt to download weather data for your current approximate location from the [open-meteo.com](https://open-meteo.com) API once your device has acquired a GPS fix. The API service is free for non-commercial use. Your location is rounded to approximately two kilometers to maintain privacy. The data is updated when you ride more than two kilometers from the location where the weather data was downloaded or after one hour at the latest. If the app cannot connect to the weather service, it will retry the download every minute. Downloading weather data should work on Karoo 2 if you have a SIM card inserted or on Karoo 3 via your phone's internet connection if you have the Karoo companion app installed.
|
||||||
|
|||||||
@ -68,6 +68,7 @@ data class HeadwindSettings(
|
|||||||
val roundLocationTo: RoundLocationSetting = RoundLocationSetting.KM_3,
|
val roundLocationTo: RoundLocationSetting = RoundLocationSetting.KM_3,
|
||||||
val forecastedKmPerHour: Int = 20,
|
val forecastedKmPerHour: Int = 20,
|
||||||
val forecastedMilesPerHour: Int = 12,
|
val forecastedMilesPerHour: Int = 12,
|
||||||
|
val lastUpdateRequested: Long? = null,
|
||||||
){
|
){
|
||||||
companion object {
|
companion object {
|
||||||
val defaultSettings = Json.encodeToString(HeadwindSettings())
|
val defaultSettings = Json.encodeToString(HeadwindSettings())
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package de.timklge.karooheadwind.screens
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -14,10 +13,13 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRow
|
import androidx.compose.material3.TabRow
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@ -37,8 +39,11 @@ import de.timklge.karooheadwind.R
|
|||||||
import de.timklge.karooheadwind.saveSettings
|
import de.timklge.karooheadwind.saveSettings
|
||||||
import de.timklge.karooheadwind.streamSettings
|
import de.timklge.karooheadwind.streamSettings
|
||||||
import io.hammerhead.karooext.KarooSystemService
|
import io.hammerhead.karooext.KarooSystemService
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(close: () -> Unit) {
|
fun MainScreen(close: () -> Unit) {
|
||||||
var karooConnected by remember { mutableStateOf(false) }
|
var karooConnected by remember { mutableStateOf(false) }
|
||||||
@ -49,8 +54,29 @@ fun MainScreen(close: () -> Unit) {
|
|||||||
var welcomeDialogVisible by remember { mutableStateOf(false) }
|
var welcomeDialogVisible by remember { mutableStateOf(false) }
|
||||||
var tabIndex by remember { mutableIntStateOf(0) }
|
var tabIndex by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
|
var isRefreshing by remember { mutableStateOf(false) }
|
||||||
|
val swipeRefreshState = rememberPullToRefreshState()
|
||||||
|
|
||||||
val tabs = listOf("Weather", "Settings")
|
val tabs = listOf("Weather", "Settings")
|
||||||
|
|
||||||
|
fun refreshData() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
isRefreshing = true
|
||||||
|
// Set the lastUpdateRequested value to trigger a weather update in the KarooHeadwindExtension
|
||||||
|
val settings = ctx.streamSettings(karooSystem).first()
|
||||||
|
saveSettings(ctx, settings.copy(lastUpdateRequested = System.currentTimeMillis()))
|
||||||
|
delay(1000) // Give some time to show the refreshing indicator
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(isRefreshing) {
|
||||||
|
if (isRefreshing) {
|
||||||
|
delay(2000) // Timeout after 2 seconds if the refresh doesn't complete
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onFinish() {
|
fun onFinish() {
|
||||||
if (tabIndex > 0){
|
if (tabIndex > 0){
|
||||||
tabIndex--
|
tabIndex--
|
||||||
@ -77,7 +103,7 @@ fun MainScreen(close: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
PullToRefreshBox(modifier = Modifier.fillMaxSize(), isRefreshing = isRefreshing, onRefresh = { refreshData() }) {
|
||||||
Column(modifier = Modifier
|
Column(modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.background)) {
|
.background(MaterialTheme.colorScheme.background)) {
|
||||||
|
|||||||
BIN
preview2.png
BIN
preview2.png
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 29 KiB |
BIN
preview3.png
Normal file
BIN
preview3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Loading…
x
Reference in New Issue
Block a user