add reload capabi lities among other things

This commit is contained in:
Henry Hiles 2023-04-06 18:36:39 -04:00
parent 49f7f4bd9a
commit ff7710143b
17 changed files with 189 additions and 139 deletions

View file

@ -12,23 +12,6 @@ import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.create
//@Module
//@InstallIn(SingletonComponent::class)
//object AppModule {
// @Provides
// @Singleton
// fun provideWeatherApi(): WeatherApi {
// return Retrofit.Builder().baseUrl("https://api.open-meteo.com")
// .addConverterFactory(MoshiConverterFactory.create()).build().create()
// }
//
// @Provides
// @Singleton
// fun provideFusedLocationProviderClient(app: Application): FusedLocationProviderClient {
// return LocationServices.getFusedLocationProviderClient(app)
// }
//}
val appModule = module {
fun provideWeatherApi(): WeatherApi {
return Retrofit.Builder().baseUrl("https://api.open-meteo.com")

View file

@ -4,15 +4,6 @@ import com.henryhiles.qweather.domain.location.LocationTracker
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
//@Module
//@InstallIn(SingletonComponent::class)
//abstract class LocationModule {
// @Binds
// @Singleton
// abstract fun bindLocationTracker(defaultLocationTracker: DefaultLocationTracker): LocationTracker
//}
val locationModule = module {
singleOf(::LocationTracker)
}

View file

@ -4,14 +4,6 @@ import com.henryhiles.qweather.domain.repository.WeatherRepository
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
//@Module
//@InstallIn(SingletonComponent::class)
//abstract class RepositoryModule {
// @Binds
// @Singleton
// abstract fun bindWeatherRepository(weatherRepository: WeatherRepository): WeatherRepository
//}
val repositoryModule = module {
singleOf(::WeatherRepository)
}

View file

@ -0,0 +1,9 @@
package com.henryhiles.qweather.domain.util
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.navigator.tab.Tab
interface NavigationTab : Tab {
@Composable
fun Actions()
}

View file

@ -1,4 +1,4 @@
package com.henryhiles.qweather.presentation.components
package com.henryhiles.qweather.presentation.components.navigation
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack

View file

@ -0,0 +1,53 @@
package com.henryhiles.qweather.presentation.components.navigation
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.navigator.tab.TabNavigator
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.tabs.SettingsTab
import com.henryhiles.qweather.presentation.tabs.TodayTab
import com.henryhiles.qweather.presentation.tabs.WeekTab
@Composable
fun BottomBar(navigator: TabNavigator) {
NavigationBar {
NavigationBarItem(
selected = navigator.current == TodayTab,
onClick = { navigator.current = TodayTab },
label = { Text(text = stringResource(id = R.string.tab_today)) },
icon = {
Icon(
imageVector = Icons.Default.Home,
contentDescription = stringResource(id = R.string.tab_today)
)
})
NavigationBarItem(
selected = navigator.current == WeekTab,
onClick = { navigator.current = WeekTab },
label = { Text(text = "Weekly") },
icon = {
Icon(
imageVector = Icons.Default.DateRange,
contentDescription = "Weekly"
)
})
NavigationBarItem(
selected = navigator.current == SettingsTab,
onClick = { navigator.current = SettingsTab },
label = { Text(text = "Settings") },
icon = {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = "Settings"
)
})
}
}

View file

@ -1,4 +1,4 @@
package com.henryhiles.qweather.presentation.components
package com.henryhiles.qweather.presentation.components.navigation
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.*
@ -7,12 +7,13 @@ import androidx.compose.runtime.Composable
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun LargeToolbar(
title: String,
title: @Composable () -> Unit,
actions: @Composable RowScope.() -> Unit = {},
backButton: Boolean = false
) {
LargeTopAppBar(
title = { Text(text = title) },
navigationIcon = { BackButton() },
title = title,
navigationIcon = { if (backButton) BackButton() },
actions = actions,
)
}

View file

@ -1,4 +1,4 @@
package com.henryhiles.qweather.presentation.components
package com.henryhiles.qweather.presentation.components.navigation
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.*
@ -7,12 +7,13 @@ import androidx.compose.runtime.Composable
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SmallToolbar(
title: String,
title: @Composable () -> Unit,
actions: @Composable RowScope.() -> Unit = {},
backButton: Boolean = true
) {
TopAppBar(
title = { Text(text = title) },
navigationIcon = { BackButton() },
title = title,
navigationIcon = { if (backButton) BackButton() },
actions = actions,
)
}

View file

@ -7,6 +7,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -17,7 +18,7 @@ import java.time.format.DateTimeFormatter
@Composable
fun WeatherDay(dailyWeatherData: DailyWeatherData) {
val formattedDate = remember {
val formattedDate by remember {
derivedStateOf {
dailyWeatherData.date.format(
DateTimeFormatter.ofPattern("E d")
@ -45,7 +46,7 @@ fun WeatherDay(dailyWeatherData: DailyWeatherData) {
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(
text = formattedDate.value
text = formattedDate
)
Text(text = "Feels like ${dailyWeatherData.apparentTemperatureMax}°C")
}

View file

@ -0,0 +1,25 @@
package com.henryhiles.qweather.presentation.screen
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import com.henryhiles.qweather.presentation.components.navigation.LargeToolbar
class AboutScreen : Screen {
@Composable
override fun Content() {
Scaffold(topBar = { LargeToolbar(title = { Text(text = "About") }, backButton = true) }) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
}
}
}
}

View file

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -13,7 +14,7 @@ import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.components.LargeToolbar
import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar
import com.henryhiles.qweather.presentation.components.settings.SettingsItemChoice
import com.henryhiles.qweather.presentation.components.settings.SettingsSwitch
import com.henryhiles.qweather.presentation.screenmodel.AppearancePreferencesScreenModel
@ -24,12 +25,16 @@ class AppearanceSettingsScreen : Screen {
override fun Content() = Screen()
@Composable
private fun Screen(
screenModel: AppearancePreferencesScreenModel = getScreenModel()
) {
private fun Screen() {
val screenModel: AppearancePreferencesScreenModel = getScreenModel()
val ctx = LocalContext.current
Scaffold(topBar = { Toolbar() }) { padding ->
Scaffold(topBar = {
SmallToolbar(
title = { Text(text = stringResource(R.string.settings_appearance)) },
backButton = true
)
}) { padding ->
Column(
modifier = Modifier
.padding(padding)
@ -52,13 +57,4 @@ class AppearanceSettingsScreen : Screen {
}
}
}
@Composable
private fun Toolbar(
) {
LargeToolbar(
title = stringResource(R.string.settings_appearance),
)
}
}

View file

@ -2,60 +2,33 @@ package com.henryhiles.qweather.presentation.screen
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.tab.TabNavigator
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.tabs.SettingsTab
import com.henryhiles.qweather.domain.util.NavigationTab
import com.henryhiles.qweather.presentation.components.navigation.BottomBar
import com.henryhiles.qweather.presentation.components.navigation.SmallToolbar
import com.henryhiles.qweather.presentation.tabs.TodayTab
import com.henryhiles.qweather.presentation.tabs.WeekTab
class MainScreen : Screen {
@Composable
override fun Content() {
TabNavigator(tab = TodayTab) { navigator ->
TabNavigator(tab = TodayTab) {
Scaffold(
topBar = {
SmallToolbar(
title = { Text(text = "QWeather") },
actions = {
(it.current as? NavigationTab)?.Actions()
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(
selected = navigator.current == TodayTab,
onClick = { navigator.current = TodayTab },
label = { Text(text = stringResource(id = R.string.tab_today)) },
icon = {
Icon(
imageVector = Icons.Default.Home,
contentDescription = stringResource(id = R.string.tab_today)
)
})
NavigationBarItem(
selected = navigator.current == WeekTab,
onClick = { navigator.current = WeekTab },
label = { Text(text = "Weekly") },
icon = {
Icon(
imageVector = Icons.Default.DateRange,
contentDescription = "Weekly"
)
})
NavigationBarItem(
selected = navigator.current == SettingsTab,
onClick = { navigator.current = SettingsTab },
label = { Text(text = "Settings") },
icon = {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = "Settings"
)
})
}
BottomBar(navigator = it)
}
) { padding ->
Box(modifier = Modifier.padding(padding)) {

View file

@ -1,5 +1,6 @@
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
@ -23,11 +24,12 @@ class DailyWeatherScreenModel constructor(
) : ScreenModel {
var state by mutableStateOf(DailyWeatherState())
private set
private var currentLocation: Location? = null
fun loadWeatherInfo() {
coroutineScope.launch {
state = state.copy(isLoading = true, error = null)
val currentLocation = locationTracker.getCurrentLocation()
currentLocation = locationTracker.getCurrentLocation()
currentLocation?.let { location ->
state = when (val result =
repository.getDailyWeatherData(location.latitude, location.longitude)) {
@ -38,7 +40,6 @@ class DailyWeatherScreenModel constructor(
error = null
)
}
is Resource.Error -> {
state.copy(
dailyWeatherData = null,

View file

@ -1,26 +1,26 @@
package com.henryhiles.qweather.presentation.tabs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Palette
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.TabOptions
import com.henryhiles.qweather.R
import com.henryhiles.qweather.presentation.components.SmallToolbar
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
object SettingsTab : Tab {
object SettingsTab : NavigationTab {
override val options: TabOptions
@Composable
get() {
@ -38,26 +38,25 @@ object SettingsTab : Tab {
@Composable
override fun Content() {
Scaffold(
topBar = {
SmallToolbar(
title = stringResource(R.string.tab_settings),
)
},
) {
Column(
modifier = Modifier
.padding(it)
.verticalScroll(rememberScrollState())
) {
Column {
SettingsCategory(
icon = Icons.Outlined.Palette,
text = stringResource(R.string.settings_appearance),
subtext = stringResource(R.string.settings_appearance_description),
destination = ::AppearanceSettingsScreen
)
}
}
SettingsCategory(
icon = Icons.Outlined.Palette,
text = stringResource(R.string.settings_appearance),
subtext = stringResource(R.string.settings_appearance_description),
destination = ::AppearanceSettingsScreen
)
}
@Composable
override fun Actions() {
val navigator = LocalNavigator.currentOrThrow.parent
IconButton(onClick = { navigator?.push(AboutScreen()) }) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = stringResource(R.string.action_open_about)
)
}
}
}

View file

@ -5,10 +5,8 @@ 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.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
@ -18,16 +16,16 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter
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.domain.util.NavigationTab
import com.henryhiles.qweather.presentation.components.weather.WeatherCard
import com.henryhiles.qweather.presentation.components.weather.WeatherForecast
import com.henryhiles.qweather.presentation.screenmodel.HourlyWeatherScreenModel
object TodayTab : Tab {
object TodayTab : NavigationTab {
override val options: TabOptions
@Composable
get() {
@ -43,7 +41,7 @@ object TodayTab : Tab {
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
@OptIn(ExperimentalPermissionsApi::class)
@Composable
override fun Content() {
val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
@ -92,4 +90,16 @@ object TodayTab : Tab {
}
}
}
@Composable
override fun Actions() {
val viewModel: HourlyWeatherScreenModel = getScreenModel()
IconButton(onClick = { viewModel.loadWeatherInfo() }) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(R.string.action_reload)
)
}
}
}

View file

@ -7,6 +7,7 @@ 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.material.icons.filled.Refresh
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -16,15 +17,15 @@ 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.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.domain.util.NavigationTab
import com.henryhiles.qweather.presentation.components.weather.WeatherDay
import com.henryhiles.qweather.presentation.screenmodel.DailyWeatherScreenModel
object WeekTab : Tab {
object WeekTab : NavigationTab {
override val options: TabOptions
@Composable
get() {
@ -92,4 +93,16 @@ object WeekTab : Tab {
}
}
}
@Composable
override fun Actions() {
val viewModel: DailyWeatherScreenModel = getScreenModel()
IconButton(onClick = { viewModel.loadWeatherInfo() }) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(R.string.action_reload)
)
}
}
}

View file

@ -6,6 +6,8 @@
<string name="action_back">Back</string>
<string name="action_confirm">Confirm</string>
<string name="action_open_about">About</string>
<string name="action_reload">Reload</string>
<string name="appearance_theme">Theme</string>
<string name="appearance_monet">Dynamic Theme</string>