Implemented first BatteryDetails.kt Composable and made TopAppBar dynamic.

This commit is contained in:
sockenklaus
2022-07-22 02:51:02 +02:00
parent f172fa4f89
commit 69f12eb78f
5 changed files with 143 additions and 51 deletions

View File

@@ -14,15 +14,22 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.sockenklaus.batterytracker.R
import com.sockenklaus.batterytracker.ui.composables.AddBattery
import com.sockenklaus.batterytracker.ui.composables.AddCharge
import com.sockenklaus.batterytracker.ui.composables.Home
import com.sockenklaus.batterytracker.ui.composables.*
import com.sockenklaus.batterytracker.ui.models.MainViewModel
import kotlinx.coroutines.launch
object Routes {
const val HOME = "home"
const val ADD_BATTERY = "add_battery"
const val ADD_CHARGE = "add_charge"
const val BATTERY_DETAILS = "battery_details"
}
@Composable
fun BatteryTracker() {
MaterialTheme {
@@ -33,17 +40,19 @@ fun BatteryTracker() {
scaffoldState = state.scaffoldState,
topBar = {
if(state.showAppBar){
TopAppBar (
title = { AppBarTitle(state = state) },
navigationIcon = {
ToggleDrawerButton(
state = state,
when(state.currentScreen){
Routes.BATTERY_DETAILS -> DetailsTopAppBar(
navController = state.navController,
changeCurrentScreen = { state.currentScreen = it }
)
else -> MainTopAppBar(
drawerState = state.scaffoldState.drawerState,
appTitle = state.appTitle
)
}
)
}
},
) { innerPadding ->
ModalDrawer(
drawerState = state.scaffoldState.drawerState,
@@ -51,46 +60,57 @@ fun BatteryTracker() {
NavListItem(
textId = R.string.nav_home,
icon = Icons.Default.Home,
route = "home",
route = Routes.HOME,
state = state,
)
NavListItem(
icon = Icons.Default.BatteryChargingFull,
textId = R.string.nav_add_charge,
route = "add_charge",
route = Routes.ADD_CHARGE,
state = state,
)
NavListItem(
icon = Icons.Default.BatteryFull,
textId = R.string.nav_add_battery,
route = "add_battery",
route = Routes.ADD_BATTERY,
state = state,
)
}
) {
NavHost(
navController = state.navController,
startDestination = "home",
startDestination = Routes.HOME,
modifier = Modifier.padding(innerPadding))
{
composable("home") { Home(state.navController) }
composable("add_charge") { AddCharge(state.navController) }
composable("add_battery") { AddBattery(state.navController) }
composable(Routes.HOME) { Home(state.navController, state) }
composable(Routes.ADD_CHARGE) { AddCharge(state.navController) }
composable(Routes.ADD_BATTERY) { AddBattery(state.navController) }
composable(
route = "${Routes.BATTERY_DETAILS}/{batteryId}",
arguments = listOf(navArgument("batteryId"){ type = NavType.IntType })
) { navBackStackEntry ->
BatteryDetails(
navController = state.navController,
batteryId = navBackStackEntry.arguments?.getInt("batteryId"),
appState = state
)
}
}
}
}
}
}
@OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class)
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AppBarTitle(
state: MainViewModel
drawerTarget: DrawerValue,
appTitle: String,
){
val text = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Closed) {
state.appTitle
val text = if(drawerTarget == DrawerValue.Closed) {
appTitle
} else {
stringResource(R.string.nav_header_title)
}
@@ -103,11 +123,11 @@ fun AppBarTitle(
@Composable
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
fun ToggleDrawerButton(
state: MainViewModel
drawerState: DrawerState
){
val scope = rememberCoroutineScope()
val icon = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Open) {
val icon = if(drawerState.targetValue == DrawerValue.Open) {
Icons.Default.Close
} else {
Icons.Default.Menu
@@ -115,8 +135,8 @@ fun ToggleDrawerButton(
IconButton(
onClick = {
if(state.scaffoldState.drawerState.isClosed) scope.launch { state.scaffoldState.drawerState.open() }
else scope.launch { state.scaffoldState.drawerState.close() }
if(drawerState.isClosed) scope.launch { drawerState.open() }
else scope.launch { drawerState.close() }
}
) {
AnimatedContent(targetState = icon) { targetState ->
@@ -134,17 +154,39 @@ fun NavListItem(
state: MainViewModel
) {
val scope = rememberCoroutineScope()
val text = if ( textId != null) stringResource(textId) else ""
val text = if ( null != textId) stringResource(textId) else ""
ListItem(
icon = { Icon(icon, null) },
text = { Text(text) },
modifier = Modifier.clickable {
modifier = Modifier
.clickable {
scope.launch {
state.appTitle = text
state.navController.navigate(route)
state.currentScreen = route
state.scaffoldState.drawerState.close()
}
}.wrapContentWidth()
}
.wrapContentWidth()
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MainTopAppBar(
drawerState: DrawerState,
appTitle: String
){
TopAppBar (
title = { AppBarTitle(
drawerTarget = drawerState.targetValue,
appTitle = appTitle
) },
navigationIcon = {
ToggleDrawerButton(
drawerState = drawerState,
)
}
)
}

View File

@@ -1,8 +1,42 @@
package com.sockenklaus.batterytracker.ui.composables
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import com.sockenklaus.batterytracker.ui.Routes
import com.sockenklaus.batterytracker.ui.models.MainViewModel
@Composable
fun BatteryDetails(){
fun BatteryDetails(
navController: NavController,
batteryId: Int? = null,
appState: MainViewModel
){
Text(batteryId.toString())
}
@Composable
fun DetailsTopAppBar(
navController: NavController,
changeCurrentScreen: (String) -> Unit
){
TopAppBar(
title = { Text("Details") },
navigationIcon = {
IconButton(
onClick = {
navController.navigate("home")
changeCurrentScreen(Routes.HOME)
}
) {
Icon(Icons.Default.ArrowBack, null)
}
}
)
}

View File

@@ -1,5 +1,7 @@
package com.sockenklaus.batterytracker.ui.composables
import android.telecom.Call
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
@@ -15,13 +17,18 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.sockenklaus.batterytracker.R
import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.ui.Routes
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
import com.sockenklaus.batterytracker.ui.models.HomeViewModel
import com.sockenklaus.batterytracker.ui.models.MainViewModel
import java.lang.reflect.GenericDeclaration
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Home(navController: NavController) {
fun Home(
navController: NavController,
appState: MainViewModel
) {
val model: HomeViewModel = viewModel()
val batteries by model.batteries.observeAsState(emptyList<Battery>())
var filterText by remember { mutableStateOf("")}
@@ -55,6 +62,10 @@ fun Home(navController: NavController) {
avg = model.getAvgChargeById(battery.id),
max = model.getMaxChargeById(battery.id)
)
},
modifier = Modifier.clickable {
navController.navigate("${Routes.BATTERY_DETAILS}/${battery.id}")
appState.currentScreen = Routes.BATTERY_DETAILS
}
)
Divider(modHorizontalPadding)
@@ -78,7 +89,7 @@ fun ListPrimaryText(
if(battery.declaredCapacity != null){
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
text = "Cap: ${battery.declaredCapacity} Ah",
text = "Cap: ${"%.2f".format(battery.declaredCapacity)} Ah",
style = MaterialTheme.typography.body2,
modifier = Modifier.alignByBaseline()
)
@@ -98,8 +109,8 @@ fun ListSecondaryText(
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(if(min != null) "Min.: $min Ah" else "")
Text(if(avg != null) "Avg.: $avg Ah" else "")
Text(if(max != null) "Max.: $max Ah" else "")
Text(if(min != null) "Min.: ${"%.2f".format(min)} Ah" else "")
Text(if(avg != null) "Avg.: ${"%.2f".format(avg)} Ah" else "")
Text(if(max != null) "Max.: ${"%.2f".format(max)} Ah" else "")
}
}

View File

@@ -13,14 +13,14 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
private val db = BatteryTrackerDB.getInstance(application)
val batteries = db.batteryDao().getBatteries().asLiveData()
private val batteriesAndCharges = db.batteryDao().getBatteriesAndCharges().asLiveData()
private val _batteriesAndCharges = db.batteryDao().getBatteriesAndCharges().asLiveData()
@Composable
fun getMinChargeById(batteryId: Int): Double? {
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
val batteryAndCharges = _batteriesAndCharges?.find{ it.battery.id == batteryId }
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
val batteryAndCharges = batteriesAndCharges?.find{ it.battery.id == batteryId }
if(batteryAndCharges != null && batteryAndCharges.charges.isNotEmpty()){
if((batteryAndCharges != null) && batteryAndCharges.charges.isNotEmpty()){
return batteryAndCharges.charges.map { it.charge }.min()
}
return null
@@ -28,10 +28,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
@Composable
fun getMaxChargeById(batteryId: Int): Double? {
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
val batteryAndCharges = _batteriesAndCharges?.find { it.battery.id == batteryId }
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
val batteryAndCharges = batteriesAndCharges?.find { it.battery.id == batteryId }
if(batteryAndCharges != null && batteryAndCharges.charges.isNotEmpty()) {
if((batteryAndCharges != null) && batteryAndCharges.charges.isNotEmpty()) {
return batteryAndCharges.charges.map { it.charge }.max()
}
return null
@@ -39,10 +39,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
@Composable
fun getAvgChargeById(batteryId: Int): Double? {
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
val batteryAndCharges = _batteriesAndCharges?.find{ it.battery.id == batteryId }
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
val batteryAndCharges = batteriesAndCharges?.find{ it.battery.id == batteryId }
if(batteryAndCharges != null && batteryAndCharges.charges.isNotEmpty()) {
if((batteryAndCharges != null) && batteryAndCharges.charges.isNotEmpty()) {
return BigDecimal(batteryAndCharges.charges.map { it.charge }.average()).setScale(2, RoundingMode.HALF_UP).toDouble()
}
return null

View File

@@ -2,14 +2,19 @@ package com.sockenklaus.batterytracker.ui.models
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.BlendMode.Companion.Screen
import androidx.lifecycle.ViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.sockenklaus.batterytracker.ui.AppBarTitle
import com.sockenklaus.batterytracker.ui.Routes
import com.sockenklaus.batterytracker.ui.ToggleDrawerButton
class MainViewModel : ViewModel() {
var showAppBar by mutableStateOf(true)
var appTitle by mutableStateOf("Home")
var currentScreen by mutableStateOf(Routes.HOME)
lateinit var navController: NavHostController
lateinit var scaffoldState: ScaffoldState