fix everything
This commit is contained in:
parent
376f28dc9d
commit
1f245aa337
11 changed files with 139 additions and 137 deletions
|
@ -4,7 +4,7 @@ import retrofit2.http.GET
|
|||
import retrofit2.http.Query
|
||||
|
||||
interface GeocodingApi {
|
||||
@GET("v1/search?count=4")
|
||||
@GET("v1/search?count=10")
|
||||
suspend fun getGeocodingData(
|
||||
@Query("name") location: String,
|
||||
): GeocodingDto
|
||||
|
|
|
@ -37,7 +37,11 @@ class QWeatherActivity : ComponentActivity() {
|
|||
WeatherAppTheme(darkTheme = isDark, monet = prefs.monet) {
|
||||
Surface(modifier = Modifier.fillMaxSize()) {
|
||||
Text(text = location.location)
|
||||
Navigator(screen = if (isLocationSet) MainScreen() else LocationPickerScreen()) {
|
||||
Navigator(
|
||||
screen = if (isLocationSet) MainScreen() else LocationPickerScreen(),
|
||||
onBackPressed = {
|
||||
it !is MainScreen
|
||||
}) {
|
||||
SlideTransition(it)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
|
@ -22,7 +21,7 @@ import com.henryhiles.qweather.domain.weather.HourlyWeatherData
|
|||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Composable
|
||||
fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) {
|
||||
fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = Modifier) {
|
||||
hour?.let {
|
||||
val formattedTime = remember(it) {
|
||||
it.time.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
|
@ -37,10 +36,17 @@ fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) {
|
|||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Today $formattedTime",
|
||||
modifier = Modifier.align(Alignment.End), color = Color.White
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = location,
|
||||
)
|
||||
Text(
|
||||
text = "Today $formattedTime",
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Image(
|
||||
painter = painterResource(id = it.weatherType.iconRes),
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState
|
||||
|
@ -25,7 +24,7 @@ fun WeatherForecast(
|
|||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Text(text = "Today", fontSize = 20.sp, color = Color.White)
|
||||
Text(text = "Today", fontSize = 20.sp)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
val rowState = rememberLazyListState(LocalDateTime.now().hour)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.MyLocation
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.*
|
||||
|
@ -24,20 +25,35 @@ import cafe.adriel.voyager.core.screen.Screen
|
|||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import com.henryhiles.qweather.R
|
||||
import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar
|
||||
import com.henryhiles.qweather.presentation.screenmodel.LocationPickerScreenModel
|
||||
|
||||
|
||||
class LocationPickerScreen : Screen {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val screenModel: LocationPickerScreenModel = getScreenModel()
|
||||
var latitude by remember { mutableStateOf(0f) }
|
||||
var longitude by remember { mutableStateOf(0f) }
|
||||
var location by remember { mutableStateOf("") }
|
||||
var latitude by remember { mutableStateOf(screenModel.prefs.latitude) }
|
||||
var longitude by remember { mutableStateOf(screenModel.prefs.longitude) }
|
||||
var location by remember { mutableStateOf(screenModel.prefs.location) }
|
||||
var locationSearch by remember { mutableStateOf("") }
|
||||
var isAboutOpen by remember { mutableStateOf(false) }
|
||||
val navigator = LocalNavigator.current
|
||||
val context = LocalContext.current
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Scaffold(floatingActionButton = {
|
||||
FloatingActionButton(onClick = {
|
||||
screenModel.prefs.location = location
|
||||
screenModel.prefs.latitude = latitude
|
||||
screenModel.prefs.longitude = longitude
|
||||
navigator?.push(MainScreen())
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(id = R.string.action_apply)
|
||||
)
|
||||
}
|
||||
}) {
|
||||
screenModel.state.error?.let {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
|
@ -51,24 +67,31 @@ class LocationPickerScreen : Screen {
|
|||
}
|
||||
},
|
||||
)
|
||||
} ?: AlertDialog(
|
||||
onDismissRequest = {},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
screenModel.prefs.location = location
|
||||
screenModel.prefs.latitude = latitude
|
||||
screenModel.prefs.longitude = longitude
|
||||
navigator?.push(MainScreen())
|
||||
},
|
||||
enabled = location != ""
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.action_apply))
|
||||
}
|
||||
},
|
||||
title = { Text(text = stringResource(id = R.string.location_choose)) },
|
||||
text = {
|
||||
Column {
|
||||
} ?: kotlin.run {
|
||||
Column {
|
||||
SmallToolbar(
|
||||
title = { Text(text = stringResource(id = R.string.location_choose)) },
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = { isAboutOpen = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Info,
|
||||
contentDescription = stringResource(id = R.string.help_screen)
|
||||
)
|
||||
}
|
||||
})
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
if (isAboutOpen) AlertDialog(
|
||||
title = { Text(text = stringResource(id = R.string.location_choose)) },
|
||||
text = { Text(text = stringResource(id = R.string.help_location_picker)) },
|
||||
onDismissRequest = { isAboutOpen = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = { isAboutOpen = false }) {
|
||||
Text(text = stringResource(id = R.string.action_confirm))
|
||||
}
|
||||
})
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(id = R.string.location)) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
|
@ -82,9 +105,7 @@ class LocationPickerScreen : Screen {
|
|||
}),
|
||||
maxLines = 1,
|
||||
value = locationSearch,
|
||||
onValueChange = {
|
||||
locationSearch = it
|
||||
},
|
||||
onValueChange = { locationSearch = it },
|
||||
trailingIcon = {
|
||||
if (locationSearch == "")
|
||||
IconButton(onClick = {
|
||||
|
@ -106,20 +127,17 @@ class LocationPickerScreen : Screen {
|
|||
contentDescription = stringResource(id = R.string.action_search)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(text = "${screenModel.state.locations != null}")
|
||||
screenModel.state.locations?.let {
|
||||
Text(
|
||||
text = "hi"
|
||||
)
|
||||
}
|
||||
|
||||
if (screenModel.state.isLoading) CircularProgressIndicator(
|
||||
modifier = Modifier.align(
|
||||
Alignment.CenterHorizontally
|
||||
)
|
||||
modifier = Modifier
|
||||
.align(
|
||||
Alignment.CenterHorizontally
|
||||
)
|
||||
.padding(16.dp)
|
||||
) else screenModel.state.locations?.let {
|
||||
LazyColumn {
|
||||
items(it) {
|
||||
|
@ -161,7 +179,7 @@ class LocationPickerScreen : Screen {
|
|||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package com.henryhiles.qweather.presentation.screenmodel
|
||||
|
||||
import android.location.Location
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import com.henryhiles.qweather.domain.location.LocationTracker
|
||||
import com.henryhiles.qweather.domain.repository.WeatherRepository
|
||||
import com.henryhiles.qweather.domain.util.Resource
|
||||
import com.henryhiles.qweather.domain.weather.DailyWeatherData
|
||||
|
@ -21,43 +19,34 @@ data class DailyWeatherState(
|
|||
|
||||
class DailyWeatherScreenModel(
|
||||
private val repository: WeatherRepository,
|
||||
private val locationTracker: LocationTracker,
|
||||
private val location: LocationPreferenceManager
|
||||
) : ScreenModel {
|
||||
var state by mutableStateOf(DailyWeatherState())
|
||||
private set
|
||||
private var currentLocation: Location? = null
|
||||
|
||||
fun loadWeatherInfo(cache: Boolean = true) {
|
||||
coroutineScope.launch {
|
||||
state = state.copy(isLoading = true, error = null)
|
||||
currentLocation = locationTracker.getCurrentLocation()
|
||||
currentLocation?.let { location ->
|
||||
state = when (val result =
|
||||
repository.getDailyWeatherData(
|
||||
lat = location.latitude.toFloat(),
|
||||
long = location.longitude.toFloat(),
|
||||
cache = cache
|
||||
)) {
|
||||
is Resource.Success -> {
|
||||
state.copy(
|
||||
dailyWeatherData = result.data,
|
||||
isLoading = false,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
is Resource.Error -> {
|
||||
state.copy(
|
||||
dailyWeatherData = null,
|
||||
isLoading = false,
|
||||
error = result.message
|
||||
)
|
||||
}
|
||||
state = when (val result =
|
||||
repository.getDailyWeatherData(
|
||||
lat = location.latitude,
|
||||
long = location.longitude,
|
||||
cache = cache
|
||||
)) {
|
||||
is Resource.Success -> {
|
||||
state.copy(
|
||||
dailyWeatherData = result.data,
|
||||
isLoading = false,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
is Resource.Error -> {
|
||||
state.copy(
|
||||
dailyWeatherData = null,
|
||||
isLoading = false,
|
||||
error = result.message
|
||||
)
|
||||
}
|
||||
} ?: kotlin.run {
|
||||
state = state.copy(
|
||||
isLoading = false,
|
||||
error = "Couldn't retrieve location. Make sure to grant permission and enable GPS."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package com.henryhiles.qweather.presentation.screenmodel
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import com.henryhiles.qweather.R
|
||||
import com.henryhiles.qweather.domain.location.LocationTracker
|
||||
import com.henryhiles.qweather.domain.repository.WeatherRepository
|
||||
import com.henryhiles.qweather.domain.util.Resource
|
||||
import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo
|
||||
|
@ -22,8 +19,7 @@ data class HourlyWeatherState(
|
|||
|
||||
class HourlyWeatherScreenModel(
|
||||
private val repository: WeatherRepository,
|
||||
private val locationTracker: LocationTracker,
|
||||
private val context: Context
|
||||
val location: LocationPreferenceManager,
|
||||
) : ScreenModel {
|
||||
var state by mutableStateOf(HourlyWeatherState())
|
||||
private set
|
||||
|
@ -31,35 +27,27 @@ class HourlyWeatherScreenModel(
|
|||
fun loadWeatherInfo(cache: Boolean = true) {
|
||||
coroutineScope.launch {
|
||||
state = state.copy(isLoading = true, error = null, selected = null)
|
||||
val currentLocation = locationTracker.getCurrentLocation()
|
||||
currentLocation?.let { location ->
|
||||
state = when (val result =
|
||||
repository.getHourlyWeatherData(
|
||||
lat = location.latitude.toFloat(),
|
||||
long = location.longitude.toFloat(),
|
||||
cache = cache
|
||||
)) {
|
||||
is Resource.Success -> {
|
||||
state.copy(
|
||||
hourlyWeatherInfo = result.data,
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
state.copy(
|
||||
hourlyWeatherInfo = null,
|
||||
isLoading = false,
|
||||
error = result.message
|
||||
)
|
||||
}
|
||||
state = when (val result =
|
||||
repository.getHourlyWeatherData(
|
||||
lat = location.latitude,
|
||||
long = location.longitude,
|
||||
cache = cache
|
||||
)) {
|
||||
is Resource.Success -> {
|
||||
state.copy(
|
||||
hourlyWeatherInfo = result.data,
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
state.copy(
|
||||
hourlyWeatherInfo = null,
|
||||
isLoading = false,
|
||||
error = result.message
|
||||
)
|
||||
}
|
||||
} ?: kotlin.run {
|
||||
state = state.copy(
|
||||
isLoading = false,
|
||||
error = context.getString(R.string.error_location)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.henryhiles.qweather.presentation.tabs
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.outlined.GpsFixed
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.Palette
|
||||
import androidx.compose.material3.Icon
|
||||
|
@ -19,6 +20,7 @@ import com.henryhiles.qweather.domain.util.NavigationTab
|
|||
import com.henryhiles.qweather.presentation.components.settings.SettingsCategory
|
||||
import com.henryhiles.qweather.presentation.screen.AboutScreen
|
||||
import com.henryhiles.qweather.presentation.screen.AppearanceSettingsScreen
|
||||
import com.henryhiles.qweather.presentation.screen.LocationPickerScreen
|
||||
|
||||
object SettingsTab : NavigationTab {
|
||||
override val options: TabOptions
|
||||
|
@ -45,6 +47,12 @@ object SettingsTab : NavigationTab {
|
|||
subtext = stringResource(R.string.settings_appearance_description),
|
||||
destination = ::AppearanceSettingsScreen
|
||||
)
|
||||
SettingsCategory(
|
||||
icon = Icons.Outlined.GpsFixed,
|
||||
text = stringResource(R.string.settings_location),
|
||||
subtext = stringResource(R.string.settings_location_description),
|
||||
destination = ::LocationPickerScreen
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.henryhiles.qweather.presentation.tabs
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -17,8 +16,6 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.henryhiles.qweather.R
|
||||
import com.henryhiles.qweather.domain.util.NavigationTab
|
||||
import com.henryhiles.qweather.presentation.components.weather.WeatherCard
|
||||
|
@ -41,18 +38,12 @@ object TodayTab : NavigationTab {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
|
||||
val permissionsState = rememberPermissionState(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
) {
|
||||
weatherViewModel.loadWeatherInfo()
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = true) {
|
||||
permissionsState.launchPermissionRequest()
|
||||
LaunchedEffect(key1 = false) {
|
||||
weatherViewModel.loadWeatherInfo()
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
|
@ -90,7 +81,8 @@ object TodayTab : NavigationTab {
|
|||
WeatherCard(
|
||||
hour = weatherViewModel.state.selected?.let {
|
||||
weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it)
|
||||
} ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData
|
||||
} ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData,
|
||||
location = weatherViewModel.location.location
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
WeatherForecast(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.henryhiles.qweather.presentation.tabs
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
|
@ -9,15 +9,15 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.henryhiles.qweather.R
|
||||
import com.henryhiles.qweather.domain.util.NavigationTab
|
||||
import com.henryhiles.qweather.presentation.components.weather.WeatherDay
|
||||
|
@ -39,21 +39,14 @@ object WeekTab : NavigationTab {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
||||
|
||||
val permissionsState = rememberPermissionState(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
) {
|
||||
LaunchedEffect(key1 = false) {
|
||||
weatherViewModel.loadWeatherInfo()
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = true) {
|
||||
permissionsState.launchPermissionRequest()
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
when {
|
||||
weatherViewModel.state.isLoading -> {
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
<string name="action_reload">Reload</string>
|
||||
<string name="action_try_again">Try Again</string>
|
||||
|
||||
<string name="selected">Selected</string>
|
||||
<string name="selected">Selected</string>x
|
||||
|
||||
<string name="help_screen">How do I use this screen?</string>
|
||||
<string name="help_location_picker">Please either tap the auto-pick button or enter a location. Then tap the apply button in the bottom left corner.</string>
|
||||
|
||||
<string name="appearance_theme">Theme</string>
|
||||
<string name="appearance_monet">Dynamic Theme</string>
|
||||
<string name="appearance_monet_description">Available on Android 12+</string>
|
||||
|
||||
<string name="settings_appearance">Appearance</string>
|
||||
<string name="settings_appearance_description">Theme, code style</string>
|
||||
<string name="settings_appearance_description">Theme, dynamic colors</string>
|
||||
<string name="settings_location">Location</string>
|
||||
<string name="settings_location_description">Location to fetch data from</string>
|
||||
|
||||
<string name="location">Location</string>
|
||||
<string name="location_string">%1$s, %2$s, %3$s</string>
|
||||
|
|
Reference in a new issue