From 69f12eb78f46481b46ba84ab7fcd062005143854 Mon Sep 17 00:00:00 2001 From: sockenklaus Date: Fri, 22 Jul 2022 02:51:02 +0200 Subject: [PATCH] Implemented first BatteryDetails.kt Composable and made TopAppBar dynamic. --- .../batterytracker/ui/BatteryTracker.kt | 110 ++++++++++++------ .../ui/composables/BatteryDetails.kt | 36 +++++- .../batterytracker/ui/composables/Home.kt | 21 +++- .../batterytracker/ui/models/HomeViewModel.kt | 20 ++-- .../batterytracker/ui/models/MainViewModel.kt | 7 +- 5 files changed, 143 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt index 5dab18d..ec44afc 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt @@ -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 { - scope.launch { - state.appTitle = text - state.navController.navigate(route) - state.scaffoldState.drawerState.close() + 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, + ) + } ) } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/BatteryDetails.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/BatteryDetails.kt index fd517b5..d400d37 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/BatteryDetails.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/BatteryDetails.kt @@ -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) + } + } + ) } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt index 711c860..aef2a37 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt @@ -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()) 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 "") } } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt index 5715cac..5b0fb51 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt @@ -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 diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt index ddb5cae..089cbda 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt @@ -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