not working, nullpointerexception

This commit is contained in:
Henry Hiles 2023-04-06 14:32:16 -04:00
parent 812e627abb
commit 78285ab5c2
24 changed files with 353 additions and 118 deletions

View file

@ -1,9 +1,10 @@
package com.henryhiles.qweather.di
import com.henryhiles.qweather.domain.remote.WeatherApi
import com.henryhiles.qweather.presentation.screenmodel.AppearanceSettingsScreenModel
import com.henryhiles.qweather.presentation.screenmodel.PreferenceManager
import com.henryhiles.qweather.presentation.screenmodel.WeatherScreenModel
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel
import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
import org.koin.core.module.dsl.factoryOf
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
@ -35,8 +36,9 @@ val appModule = module {
}
singleOf(::provideWeatherApi)
singleOf(::PreferenceManager)
factoryOf(::AppearanceSettingsScreenModel)
singleOf(::AppearancePreferenceManager)
factoryOf(::WeatherScreenModel)
factoryOf(::AppearancePreferencesScreenModel)
factoryOf(::HourlyWeatherScreenModel)
factoryOf(::DailyWeatherScreenModel)
}

View file

@ -1,35 +1,56 @@
package com.henryhiles.qweather.domain.mappers
import com.henryhiles.qweather.domain.remote.WeatherDataDto
import com.henryhiles.qweather.domain.remote.DailyWeatherDataDto
import com.henryhiles.qweather.domain.remote.HourlyWeatherDataDto
import com.henryhiles.qweather.domain.remote.WeatherDto
import com.henryhiles.qweather.domain.weather.WeatherData
import com.henryhiles.qweather.domain.weather.WeatherInfo
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo
import com.henryhiles.qweather.domain.weather.WeatherType
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.math.roundToInt
private data class IndexedWeatherData(val index: Int, val data: WeatherData)
private data class IndexedHourlyWeatherData(val index: Int, val data: HourlyWeatherData)
fun WeatherDataDto.toWeatherDataMap(): Map<Int, List<WeatherData>> {
return time.mapIndexed { index, time ->
IndexedWeatherData(
index = index, data = WeatherData(
fun HourlyWeatherDataDto.toHourlyWeatherDataMap(): Map<Int, List<HourlyWeatherData>> {
return times.mapIndexed { index, time ->
IndexedHourlyWeatherData(
index = index,
data = HourlyWeatherData(
time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME),
temperatureCelsius = temperatures[index].toInt(),
pressure = pressures[index],
windSpeed = windSpeeds[index],
humidity = humidities[index],
temperature = temperatures[index].roundToInt(),
apparentTemperature = apparentTemperatures[index].roundToInt(),
windSpeed = windSpeeds[index].roundToInt(),
precipitationProbability = precipitationProbabilities[index],
weatherType = WeatherType.fromWMO(weatherCodes[index])
)
)
}.groupBy { it.index / 24 }.mapValues { entry -> entry.value.map { it.data } }
}
fun WeatherDto.toWeatherInfo(): WeatherInfo {
val weatherDataMap = weatherData.toWeatherDataMap()
fun DailyWeatherDataDto.toDailyWeatherDataMap(): List<DailyWeatherData> {
return dates.mapIndexed { index, date ->
DailyWeatherData(
date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE),
weatherType = WeatherType.fromWMO(weatherCodes[index]),
apparentTemperatureMax = apparentTemperaturesMax[index].roundToInt(),
apparentTemperatureMin = apparentTemperaturesMin[index].roundToInt(),
temperatureMax = temperaturesMax[index].roundToInt(),
temperatureMin = temperaturesMin[index].roundToInt()
)
}
}
fun WeatherDto.toHourlyWeatherInfo(): HourlyWeatherInfo {
val weatherDataMap = hourlyWeatherData.toHourlyWeatherDataMap()
val now = LocalDateTime.now()
val currentWeatherData = weatherDataMap[0]?.find {
it.time.hour == now.hour
}
return WeatherInfo(weatherDataPerDay = weatherDataMap, currentWeatherData = currentWeatherData)
return HourlyWeatherInfo(
weatherDataPerDay = weatherDataMap,
currentWeatherData = currentWeatherData
)
}

View file

@ -0,0 +1,18 @@
package com.henryhiles.qweather.domain.remote
import com.squareup.moshi.Json
data class DailyWeatherDataDto(
@field:Json(name = "time")
val dates: List<String>,
@field:Json(name = "weathercode")
val weatherCodes: List<Int>,
@field:Json(name = "temperature_2m_max")
val temperaturesMax: List<Double>,
@field:Json(name = "temperature_2m_min")
val temperaturesMin: List<Double>,
@field:Json(name = "apparent_temperature_max")
val apparentTemperaturesMax: List<Double>,
@field:Json(name = "apparent_temperature_min")
val apparentTemperaturesMin: List<Double>
)

View file

@ -2,16 +2,17 @@ package com.henryhiles.qweather.domain.remote
import com.squareup.moshi.Json
data class WeatherDataDto(
val time: List<String>,
data class HourlyWeatherDataDto(
@field:Json(name = "time")
val times: List<String>,
@field:Json(name = "temperature_2m")
val temperatures: List<Double>,
@field:Json(name = "apparent_temperature")
val apparentTemperatures: List<Double>,
@field:Json(name = "weathercode")
val weatherCodes: List<Int>,
@field:Json(name = "pressure_msl")
val pressures: List<Double>,
@field:Json(name = "precipitation_probability")
val precipitationProbabilities: List<Int>,
@field:Json(name = "windspeed_10m")
val windSpeeds: List<Double>,
@field:Json(name = "relativehumidity_2m")
val humidities: List<Double>
)

View file

@ -3,10 +3,17 @@ package com.henryhiles.qweather.domain.remote
import retrofit2.http.GET
import retrofit2.http.Query
const val DAILY =
"daily=weathercode,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min"
const val HOURLY =
"hourly=temperature_2m,apparent_temperature,precipitation_probability,weathercode,windspeed_10m"
const val TIMEZONE = "timezone=auto"
const val FORECAST_DAYS = "forecast_days=14"
interface WeatherApi {
@GET("v1/forecast?latitude=43.72&longitude=-79.35&hourly=temperature_2m&daily=weathercode,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min&timezone=auto")
@GET("v1/forecast?$HOURLY&$DAILY&$TIMEZONE&$FORECAST_DAYS")
suspend fun getWeatherData(
@Query("latitude") lat: Double,
@Query("longitude") long: Double
@Query("longitude") long: Double,
): WeatherDto
}

View file

@ -4,5 +4,8 @@ import com.squareup.moshi.Json
data class WeatherDto(
@field:Json(name = "hourly")
val weatherData: WeatherDataDto
val hourlyWeatherData: HourlyWeatherDataDto,
@field:Json(name = "daily")
val dailyWeatherData: DailyWeatherDataDto
)

View file

@ -1,14 +1,35 @@
package com.henryhiles.qweather.domain.repository
import com.henryhiles.qweather.domain.mappers.toWeatherInfo
import com.henryhiles.qweather.domain.mappers.toDailyWeatherDataMap
import com.henryhiles.qweather.domain.mappers.toHourlyWeatherInfo
import com.henryhiles.qweather.domain.remote.WeatherApi
import com.henryhiles.qweather.domain.util.Resource
import com.henryhiles.qweather.domain.weather.WeatherInfo
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherInfo
class WeatherRepository constructor(private val api: WeatherApi) {
suspend fun getWeatherData(lat: Double, long: Double): Resource<WeatherInfo> {
suspend fun getHourlyWeatherData(lat: Double, long: Double): Resource<HourlyWeatherInfo> {
return try {
Resource.Success(data = api.getWeatherData(lat = lat, long = long).toWeatherInfo())
Resource.Success(
data = api.getWeatherData(lat = lat, long = long).toHourlyWeatherInfo()
)
} catch (e: Exception) {
e.printStackTrace()
Resource.Error(e.message ?: "An unknown error occurred.")
}
}
suspend fun getDailyWeatherData(
lat: Double,
long: Double
): Resource<List<DailyWeatherData>> {
return try {
Resource.Success(
data = api.getWeatherData(
lat = lat,
long = long
).dailyWeatherData.toDailyWeatherDataMap()
)
} catch (e: Exception) {
e.printStackTrace()
Resource.Error(e.message ?: "An unknown error occurred.")

View file

@ -0,0 +1,12 @@
package com.henryhiles.qweather.domain.weather
import java.time.LocalDate
data class DailyWeatherData(
val date: LocalDate,
val weatherType: WeatherType,
val temperatureMax: Int,
val temperatureMin: Int,
val apparentTemperatureMax: Int,
val apparentTemperatureMin: Int,
)

View file

@ -0,0 +1,12 @@
package com.henryhiles.qweather.domain.weather
import java.time.LocalDateTime
data class HourlyWeatherData(
val time: LocalDateTime,
val temperature: Int,
val apparentTemperature: Int,
val weatherType: WeatherType,
val precipitationProbability: Int?,
val windSpeed: Int,
)

View file

@ -0,0 +1,6 @@
package com.henryhiles.qweather.domain.weather
data class HourlyWeatherInfo(
val weatherDataPerDay: Map<Int, List<HourlyWeatherData>>,
val currentWeatherData: HourlyWeatherData?
)

View file

@ -1,12 +0,0 @@
package com.henryhiles.qweather.domain.weather
import java.time.LocalDateTime
data class WeatherData(
val time: LocalDateTime,
val temperatureCelsius: Int,
val pressure: Double,
val windSpeed: Double,
val humidity: Double,
val weatherType: WeatherType
)

View file

@ -1,6 +0,0 @@
package com.henryhiles.qweather.domain.weather
data class WeatherInfo(
val weatherDataPerDay: Map<Int, List<WeatherData>>,
val currentWeatherData: WeatherData?
)

View file

@ -9,13 +9,13 @@ import androidx.compose.material3.Surface
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.SlideTransition
import com.henryhiles.qweather.presentation.screen.MainScreen
import com.henryhiles.qweather.presentation.screenmodel.PreferenceManager
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferenceManager
import com.henryhiles.qweather.presentation.screenmodel.Theme
import com.henryhiles.qweather.presentation.ui.theme.WeatherAppTheme
import org.koin.android.ext.android.inject
class QWeatherActivity : ComponentActivity() {
private val prefs: PreferenceManager by inject()
private val prefs: AppearancePreferenceManager by inject()
@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {

View file

@ -3,6 +3,8 @@ package com.henryhiles.qweather.presentation.components.weather
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Thermostat
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -16,14 +18,12 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.components.WeatherDataDisplay
import com.henryhiles.qweather.presentation.screenmodel.WeatherState
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState
import java.time.format.DateTimeFormatter
import kotlin.math.roundToInt
@Composable
fun WeatherCard(state: WeatherState, modifier: Modifier = Modifier) {
state.weatherInfo?.currentWeatherData?.let {
fun WeatherCard(state: HourlyWeatherState, modifier: Modifier = Modifier) {
state.hourlyWeatherInfo?.currentWeatherData?.let {
val formattedTime = remember(it) {
it.time.format(DateTimeFormatter.ofPattern("HH:mm"))
}
@ -48,7 +48,7 @@ fun WeatherCard(state: WeatherState, modifier: Modifier = Modifier) {
modifier = Modifier.width(200.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(text = "${it.temperatureCelsius}°C", fontSize = 50.sp)
Text(text = "${it.temperature}°C", fontSize = 50.sp)
Spacer(modifier = Modifier.height(16.dp))
Text(text = it.weatherType.weatherDesc, fontSize = 20.sp)
Spacer(modifier = Modifier.height(32.dp))
@ -57,19 +57,19 @@ fun WeatherCard(state: WeatherState, modifier: Modifier = Modifier) {
horizontalArrangement = Arrangement.SpaceAround
) {
WeatherDataDisplay(
value = it.pressure.roundToInt(),
unit = "hpa",
icon = ImageVector.vectorResource(id = R.drawable.ic_pressure),
description = "Pressure",
value = it.apparentTemperature,
unit = "°C",
icon = Icons.Default.Thermostat,
description = "Feels like",
)
WeatherDataDisplay(
value = it.humidity.roundToInt(),
value = it.precipitationProbability,
unit = "%",
icon = ImageVector.vectorResource(id = R.drawable.ic_drop),
description = "Humidity"
description = "Chance of precipitation"
)
WeatherDataDisplay(
value = it.windSpeed.roundToInt(),
value = it.windSpeed,
unit = "km/h",
icon = ImageVector.vectorResource(id = R.drawable.ic_wind),
description = "Wind Speed",

View file

@ -1,4 +1,4 @@
package com.henryhiles.qweather.presentation.components
package com.henryhiles.qweather.presentation.components.weather
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -10,11 +10,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.henryhiles.qweather.R
@Composable
fun WeatherDataDisplay(
value: Int,
value: Int?,
unit: String,
icon: ImageVector,
description: String,
@ -24,10 +26,9 @@ fun WeatherDataDisplay(
Icon(
imageVector = icon,
contentDescription = description,
// tint = MaterialTheme.colorScheme.onSecondary,
modifier = Modifier.size(25.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(text = "$value$unit")
Text(text = if (value == null) stringResource(id = R.string.unknown) else "$value$unit")
}
}

View file

@ -1,4 +1,4 @@
package com.henryhiles.qweather.presentation.components
package com.henryhiles.qweather.presentation.components.weather
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
@ -10,13 +10,12 @@ 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.components.weather.WeatherHour
import com.henryhiles.qweather.presentation.screenmodel.WeatherState
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherState
import java.time.LocalDateTime
@Composable
fun WeatherForecast(state: WeatherState, modifier: Modifier = Modifier) {
state.weatherInfo?.weatherDataPerDay?.get(0)?.let {
fun WeatherForecast(state: HourlyWeatherState, modifier: Modifier = Modifier) {
state.hourlyWeatherInfo?.weatherDataPerDay?.get(0)?.let {
Column(
modifier = modifier
.fillMaxWidth()

View file

@ -11,11 +11,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.henryhiles.qweather.domain.weather.WeatherData
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
import java.time.format.DateTimeFormatter
@Composable
fun WeatherHour(data: WeatherData, modifier: Modifier = Modifier) {
fun WeatherHour(data: HourlyWeatherData, modifier: Modifier = Modifier) {
data.let {
val formattedTime = remember(it) {
it.time.format(DateTimeFormatter.ofPattern("HH:mm"))
@ -29,10 +29,10 @@ fun WeatherHour(data: WeatherData, modifier: Modifier = Modifier) {
Text(text = formattedTime)
Image(
painter = painterResource(id = it.weatherType.iconRes),
contentDescription = "Image of ${data.weatherType.weatherDesc}",
contentDescription = "Image of ${it.weatherType.weatherDesc}",
modifier = Modifier.width(40.dp)
)
Text(text = "${it.temperatureCelsius}°C")
Text(text = "${it.temperature}°C")
}
}

View file

@ -16,7 +16,7 @@ import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.components.LargeToolbar
import com.henryhiles.qweather.presentation.components.settings.SettingsItemChoice
import com.henryhiles.qweather.presentation.components.settings.SettingsSwitch
import com.henryhiles.qweather.presentation.screenmodel.AppearanceSettingsScreenModel
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel
class AppearanceSettingsScreen : Screen {
@ -25,7 +25,7 @@ class AppearanceSettingsScreen : Screen {
@Composable
private fun Screen(
screenModel: AppearanceSettingsScreenModel = getScreenModel()
screenModel: AppearancePreferencesScreenModel = getScreenModel()
) {
val ctx = LocalContext.current

View file

@ -7,7 +7,7 @@ import cafe.adriel.voyager.core.model.ScreenModel
import com.henryhiles.qweather.R
import com.henryhiles.qweather.domain.manager.BasePreferenceManager
class AppearanceSettingsManager(context: Context) :
class AppearancePreferenceManager(context: Context) :
BasePreferenceManager(context.getSharedPreferences("prefs", Context.MODE_PRIVATE)) {
var theme by enumPreference("theme", Theme.SYSTEM)
var monet by booleanPreference("monet", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
@ -19,6 +19,6 @@ enum class Theme(@StringRes val label: Int) {
DARK(R.string.theme_dark);
}
class AppearanceSettingsScreenModel(
val prefs: AppearanceSettingsManager
class AppearancePreferencesScreenModel(
val prefs: AppearancePreferenceManager
) : ScreenModel

View file

@ -8,20 +8,20 @@ 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.WeatherInfo
import com.henryhiles.qweather.domain.weather.DailyWeatherData
import kotlinx.coroutines.launch
data class WeatherState(
val weatherInfo: WeatherInfo? = null,
data class DailyWeatherState(
val dailyWeatherData: List<DailyWeatherData>? = null,
val isLoading: Boolean = false,
val error: String? = null
)
class WeatherScreenModel constructor(
class DailyWeatherScreenModel constructor(
private val repository: WeatherRepository,
private val locationTracker: LocationTracker,
) : ScreenModel {
var state by mutableStateOf(WeatherState())
var state by mutableStateOf(DailyWeatherState())
private set
fun loadWeatherInfo() {
@ -30,10 +30,10 @@ class WeatherScreenModel constructor(
val currentLocation = locationTracker.getCurrentLocation()
currentLocation?.let { location ->
state = when (val result =
repository.getWeatherData(location.latitude, location.longitude)) {
repository.getDailyWeatherData(location.latitude, location.longitude)) {
is Resource.Success -> {
state.copy(
weatherInfo = result.data,
dailyWeatherData = result.data,
isLoading = false,
error = null
)
@ -41,7 +41,7 @@ class WeatherScreenModel constructor(
is Resource.Error -> {
state.copy(
weatherInfo = null,
dailyWeatherData = null,
isLoading = false,
error = result.message
)

View file

@ -0,0 +1,58 @@
package com.henryhiles.qweather.presentation.screenmodel
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.HourlyWeatherInfo
import kotlinx.coroutines.launch
data class HourlyWeatherState(
val hourlyWeatherInfo: HourlyWeatherInfo? = null,
val isLoading: Boolean = false,
val error: String? = null
)
class HourlyWeatherScreenModel constructor(
private val repository: WeatherRepository,
private val locationTracker: LocationTracker,
) : ScreenModel {
var state by mutableStateOf(HourlyWeatherState())
private set
fun loadWeatherInfo() {
coroutineScope.launch {
state = state.copy(isLoading = true, error = null)
val currentLocation = locationTracker.getCurrentLocation()
currentLocation?.let { location ->
state = when (val result =
repository.getHourlyWeatherData(location.latitude, location.longitude)) {
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 = "Couldn't retrieve location. Make sure to grant permission and enable GPS."
)
}
}
}
}

View file

@ -5,7 +5,10 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material3.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
@ -21,8 +24,8 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.components.weather.WeatherCard
import com.henryhiles.qweather.presentation.components.WeatherForecast
import com.henryhiles.qweather.presentation.screenmodel.WeatherScreenModel
import com.henryhiles.qweather.presentation.components.weather.WeatherForecast
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
object TodayTab : Tab {
override val options: TabOptions
@ -43,7 +46,7 @@ object TodayTab : Tab {
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
@Composable
override fun Content() {
val weatherViewModel = getScreenModel<WeatherScreenModel>()
val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
val permissionsState = rememberPermissionState(
Manifest.permission.ACCESS_FINE_LOCATION,
@ -65,27 +68,16 @@ object TodayTab : Tab {
)
}
weatherViewModel.state.error != null -> {
AlertDialog(onDismissRequest = {}) {
Surface(
shape = MaterialTheme.shapes.large
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "An error occurred",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
AlertDialog(
onDismissRequest = {},
confirmButton = {},
title = { Text(text = "An error occurred") }, text = {
SelectionContainer {
Text(
text = weatherViewModel.state.error!!,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(16.dp)
)
}
}
}
}
})
}
else -> {
Column(

View file

@ -1,15 +1,30 @@
package com.henryhiles.qweather.presentation.tabs
import android.Manifest
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material3.Text
import androidx.compose.material3.*
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.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.koin.getScreenModel
import cafe.adriel.voyager.navigator.tab.Tab
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.presentation.screenmodel.DailyWeatherScreenModel
object WeekTab : Tab {
override val options: TabOptions
@ -27,8 +42,91 @@ object WeekTab : Tab {
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
override fun Content() {
Text(text = "Week Screen")
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
val permissionsState = rememberPermissionState(
Manifest.permission.ACCESS_FINE_LOCATION,
) {
weatherViewModel.loadWeatherInfo()
}
LaunchedEffect(key1 = true) {
permissionsState.launchPermissionRequest()
}
Box(modifier = Modifier.fillMaxSize()) {
when {
weatherViewModel.state.isLoading -> {
CircularProgressIndicator(
modifier = Modifier.align(
Alignment.Center
)
)
}
weatherViewModel.state.error != null -> {
AlertDialog(
onDismissRequest = {},
confirmButton = {},
title = { Text(text = "An error occurred") },
text = {
SelectionContainer {
Text(
text = weatherViewModel.state.error!!,
)
}
})
}
else -> {
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
weatherViewModel.state.dailyWeatherData?.let { data ->
items(data) {
Card(
modifier = Modifier.padding(
horizontal = 16.dp,
vertical = 8.dp
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = it.weatherType.iconRes),
contentDescription = "Image of ${it.weatherType}",
modifier = Modifier.width(48.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = //it.date.dayOfWeek
when (it.date.dayOfWeek.value) {
1 -> "Monday"
2 -> "Tuesday"
3 -> "Wednesday"
4 -> "Thursday"
5 -> "Friday"
6 -> "Saturday"
7 -> "Sunday"
else -> "Unknown"
}
)
Spacer(modifier = Modifier.width(24.dp))
Text(text = it.temperatureMax.toString())
}
}
}
}
}
}
}
}
}
}

View file

@ -17,4 +17,6 @@
<string name="theme_system">System</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="unknown">Unknown</string>
</resources>