fix everything

This commit is contained in:
Henry Hiles 2023-04-17 21:11:24 -04:00
parent 376f28dc9d
commit 1f245aa337
11 changed files with 139 additions and 137 deletions

View file

@ -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

View file

@ -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)
}
}

View file

@ -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),

View file

@ -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)

View file

@ -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 {
}
}
}
)
}
}
}
}

View file

@ -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."
)
}
}
}

View file

@ -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)
)
}
}
}

View file

@ -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
)
}
}

View file

@ -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(

View file

@ -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 -> {

View file

@ -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>