Implemented first BatteryDetails.kt Composable and made TopAppBar dynamic.
This commit is contained in:
@@ -14,15 +14,22 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.navArgument
|
||||||
import com.sockenklaus.batterytracker.R
|
import com.sockenklaus.batterytracker.R
|
||||||
import com.sockenklaus.batterytracker.ui.composables.AddBattery
|
import com.sockenklaus.batterytracker.ui.composables.*
|
||||||
import com.sockenklaus.batterytracker.ui.composables.AddCharge
|
|
||||||
import com.sockenklaus.batterytracker.ui.composables.Home
|
|
||||||
import com.sockenklaus.batterytracker.ui.models.MainViewModel
|
import com.sockenklaus.batterytracker.ui.models.MainViewModel
|
||||||
import kotlinx.coroutines.launch
|
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
|
@Composable
|
||||||
fun BatteryTracker() {
|
fun BatteryTracker() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
@@ -33,17 +40,19 @@ fun BatteryTracker() {
|
|||||||
scaffoldState = state.scaffoldState,
|
scaffoldState = state.scaffoldState,
|
||||||
|
|
||||||
topBar = {
|
topBar = {
|
||||||
if(state.showAppBar){
|
when(state.currentScreen){
|
||||||
TopAppBar (
|
|
||||||
title = { AppBarTitle(state = state) },
|
Routes.BATTERY_DETAILS -> DetailsTopAppBar(
|
||||||
navigationIcon = {
|
navController = state.navController,
|
||||||
ToggleDrawerButton(
|
changeCurrentScreen = { state.currentScreen = it }
|
||||||
state = state,
|
)
|
||||||
)
|
|
||||||
}
|
else -> MainTopAppBar(
|
||||||
|
drawerState = state.scaffoldState.drawerState,
|
||||||
|
appTitle = state.appTitle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
ModalDrawer(
|
ModalDrawer(
|
||||||
drawerState = state.scaffoldState.drawerState,
|
drawerState = state.scaffoldState.drawerState,
|
||||||
@@ -51,46 +60,57 @@ fun BatteryTracker() {
|
|||||||
NavListItem(
|
NavListItem(
|
||||||
textId = R.string.nav_home,
|
textId = R.string.nav_home,
|
||||||
icon = Icons.Default.Home,
|
icon = Icons.Default.Home,
|
||||||
route = "home",
|
route = Routes.HOME,
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
|
|
||||||
NavListItem(
|
NavListItem(
|
||||||
icon = Icons.Default.BatteryChargingFull,
|
icon = Icons.Default.BatteryChargingFull,
|
||||||
textId = R.string.nav_add_charge,
|
textId = R.string.nav_add_charge,
|
||||||
route = "add_charge",
|
route = Routes.ADD_CHARGE,
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
|
|
||||||
NavListItem(
|
NavListItem(
|
||||||
icon = Icons.Default.BatteryFull,
|
icon = Icons.Default.BatteryFull,
|
||||||
textId = R.string.nav_add_battery,
|
textId = R.string.nav_add_battery,
|
||||||
route = "add_battery",
|
route = Routes.ADD_BATTERY,
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = state.navController,
|
navController = state.navController,
|
||||||
startDestination = "home",
|
startDestination = Routes.HOME,
|
||||||
modifier = Modifier.padding(innerPadding))
|
modifier = Modifier.padding(innerPadding))
|
||||||
{
|
{
|
||||||
composable("home") { Home(state.navController) }
|
composable(Routes.HOME) { Home(state.navController, state) }
|
||||||
composable("add_charge") { AddCharge(state.navController) }
|
composable(Routes.ADD_CHARGE) { AddCharge(state.navController) }
|
||||||
composable("add_battery") { AddBattery(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
|
@Composable
|
||||||
fun AppBarTitle(
|
fun AppBarTitle(
|
||||||
state: MainViewModel
|
drawerTarget: DrawerValue,
|
||||||
|
appTitle: String,
|
||||||
){
|
){
|
||||||
val text = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Closed) {
|
val text = if(drawerTarget == DrawerValue.Closed) {
|
||||||
state.appTitle
|
appTitle
|
||||||
} else {
|
} else {
|
||||||
stringResource(R.string.nav_header_title)
|
stringResource(R.string.nav_header_title)
|
||||||
}
|
}
|
||||||
@@ -103,11 +123,11 @@ fun AppBarTitle(
|
|||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
|
||||||
fun ToggleDrawerButton(
|
fun ToggleDrawerButton(
|
||||||
state: MainViewModel
|
drawerState: DrawerState
|
||||||
){
|
){
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val icon = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Open) {
|
val icon = if(drawerState.targetValue == DrawerValue.Open) {
|
||||||
Icons.Default.Close
|
Icons.Default.Close
|
||||||
} else {
|
} else {
|
||||||
Icons.Default.Menu
|
Icons.Default.Menu
|
||||||
@@ -115,8 +135,8 @@ fun ToggleDrawerButton(
|
|||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if(state.scaffoldState.drawerState.isClosed) scope.launch { state.scaffoldState.drawerState.open() }
|
if(drawerState.isClosed) scope.launch { drawerState.open() }
|
||||||
else scope.launch { state.scaffoldState.drawerState.close() }
|
else scope.launch { drawerState.close() }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
AnimatedContent(targetState = icon) { targetState ->
|
AnimatedContent(targetState = icon) { targetState ->
|
||||||
@@ -134,17 +154,39 @@ fun NavListItem(
|
|||||||
state: MainViewModel
|
state: MainViewModel
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val text = if ( textId != null) stringResource(textId) else ""
|
val text = if ( null != textId) stringResource(textId) else ""
|
||||||
|
|
||||||
ListItem(
|
ListItem(
|
||||||
icon = { Icon(icon, null) },
|
icon = { Icon(icon, null) },
|
||||||
text = { Text(text) },
|
text = { Text(text) },
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier
|
||||||
scope.launch {
|
.clickable {
|
||||||
state.appTitle = text
|
scope.launch {
|
||||||
state.navController.navigate(route)
|
state.appTitle = text
|
||||||
state.scaffoldState.drawerState.close()
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,42 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.composables
|
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.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.sockenklaus.batterytracker.ui.Routes
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.MainViewModel
|
||||||
|
|
||||||
@Composable
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.composables
|
package com.sockenklaus.batterytracker.ui.composables
|
||||||
|
|
||||||
|
import android.telecom.Call
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
@@ -15,13 +17,18 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.sockenklaus.batterytracker.R
|
import com.sockenklaus.batterytracker.R
|
||||||
import com.sockenklaus.batterytracker.room.entities.Battery
|
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.composables.util.MyOutlinedTextFieldWithSuffix
|
||||||
import com.sockenklaus.batterytracker.ui.models.HomeViewModel
|
import com.sockenklaus.batterytracker.ui.models.HomeViewModel
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.MainViewModel
|
||||||
import java.lang.reflect.GenericDeclaration
|
import java.lang.reflect.GenericDeclaration
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Home(navController: NavController) {
|
fun Home(
|
||||||
|
navController: NavController,
|
||||||
|
appState: MainViewModel
|
||||||
|
) {
|
||||||
val model: HomeViewModel = viewModel()
|
val model: HomeViewModel = viewModel()
|
||||||
val batteries by model.batteries.observeAsState(emptyList<Battery>())
|
val batteries by model.batteries.observeAsState(emptyList<Battery>())
|
||||||
var filterText by remember { mutableStateOf("")}
|
var filterText by remember { mutableStateOf("")}
|
||||||
@@ -55,6 +62,10 @@ fun Home(navController: NavController) {
|
|||||||
avg = model.getAvgChargeById(battery.id),
|
avg = model.getAvgChargeById(battery.id),
|
||||||
max = model.getMaxChargeById(battery.id)
|
max = model.getMaxChargeById(battery.id)
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
navController.navigate("${Routes.BATTERY_DETAILS}/${battery.id}")
|
||||||
|
appState.currentScreen = Routes.BATTERY_DETAILS
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Divider(modHorizontalPadding)
|
Divider(modHorizontalPadding)
|
||||||
@@ -78,7 +89,7 @@ fun ListPrimaryText(
|
|||||||
if(battery.declaredCapacity != null){
|
if(battery.declaredCapacity != null){
|
||||||
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
|
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
|
||||||
Text(
|
Text(
|
||||||
text = "Cap: ${battery.declaredCapacity} Ah",
|
text = "Cap: ${"%.2f".format(battery.declaredCapacity)} Ah",
|
||||||
style = MaterialTheme.typography.body2,
|
style = MaterialTheme.typography.body2,
|
||||||
modifier = Modifier.alignByBaseline()
|
modifier = Modifier.alignByBaseline()
|
||||||
)
|
)
|
||||||
@@ -98,8 +109,8 @@ fun ListSecondaryText(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text(if(min != null) "Min.: $min Ah" else "")
|
Text(if(min != null) "Min.: ${"%.2f".format(min)} Ah" else "")
|
||||||
Text(if(avg != null) "Avg.: $avg Ah" else "")
|
Text(if(avg != null) "Avg.: ${"%.2f".format(avg)} Ah" else "")
|
||||||
Text(if(max != null) "Max.: $max Ah" else "")
|
Text(if(max != null) "Max.: ${"%.2f".format(max)} Ah" else "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,14 +13,14 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
private val db = BatteryTrackerDB.getInstance(application)
|
private val db = BatteryTrackerDB.getInstance(application)
|
||||||
val batteries = db.batteryDao().getBatteries().asLiveData()
|
val batteries = db.batteryDao().getBatteries().asLiveData()
|
||||||
private val batteriesAndCharges = db.batteryDao().getBatteriesAndCharges().asLiveData()
|
private val _batteriesAndCharges = db.batteryDao().getBatteriesAndCharges().asLiveData()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getMinChargeById(batteryId: Int): Double? {
|
fun getMinChargeById(batteryId: Int): Double? {
|
||||||
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
|
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
|
||||||
val batteryAndCharges = _batteriesAndCharges?.find{ it.battery.id == batteryId }
|
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 batteryAndCharges.charges.map { it.charge }.min()
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -28,10 +28,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getMaxChargeById(batteryId: Int): Double? {
|
fun getMaxChargeById(batteryId: Int): Double? {
|
||||||
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
|
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
|
||||||
val batteryAndCharges = _batteriesAndCharges?.find { it.battery.id == batteryId }
|
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 batteryAndCharges.charges.map { it.charge }.max()
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -39,10 +39,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getAvgChargeById(batteryId: Int): Double? {
|
fun getAvgChargeById(batteryId: Int): Double? {
|
||||||
val _batteriesAndCharges by batteriesAndCharges.observeAsState()
|
val batteriesAndCharges by _batteriesAndCharges.observeAsState()
|
||||||
val batteryAndCharges = _batteriesAndCharges?.find{ it.battery.id == batteryId }
|
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 BigDecimal(batteryAndCharges.charges.map { it.charge }.average()).setScale(2, RoundingMode.HALF_UP).toDouble()
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -2,14 +2,19 @@ package com.sockenklaus.batterytracker.ui.models
|
|||||||
|
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.graphics.BlendMode.Companion.Screen
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
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() {
|
class MainViewModel : ViewModel() {
|
||||||
|
|
||||||
var showAppBar by mutableStateOf(true)
|
|
||||||
var appTitle by mutableStateOf("Home")
|
var appTitle by mutableStateOf("Home")
|
||||||
|
var currentScreen by mutableStateOf(Routes.HOME)
|
||||||
|
|
||||||
lateinit var navController: NavHostController
|
lateinit var navController: NavHostController
|
||||||
lateinit var scaffoldState: ScaffoldState
|
lateinit var scaffoldState: ScaffoldState
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user