From f5e3d51378b8886ce1b596607b4614ce87b11946 Mon Sep 17 00:00:00 2001 From: sockenklaus Date: Tue, 19 Jul 2022 22:23:19 +0200 Subject: [PATCH] Implemented AddChargeFragment.kt functionality --- .../add_battery/AddBatteryFragment.kt | 49 ++--------- .../add_battery/AddBatteryViewModel.kt | 24 +++-- .../fragments/add_charge/AddChargeFragment.kt | 85 ++++++++++++------ .../add_charge/AddChargeViewModel.kt | 45 ++++++++-- .../ui/fragments/composables/Composables.kt | 88 +++++++++++++++++++ .../main/res/navigation/mobile_navigation.xml | 12 ++- app/src/main/res/values/strings.xml | 1 + 7 files changed, 220 insertions(+), 84 deletions(-) create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/composables/Composables.kt diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt index 236df3f..720db06 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt @@ -17,11 +17,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.findNavController import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.databinding.FragmentAddBatteryBinding import com.sockenklaus.batterytracker.room.entities.Battery +import com.sockenklaus.batterytracker.ui.fragments.composables.MyOutlinedTextField import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme -import com.sockenklaus.batterytracker.ui.theme.Gray500 import com.sockenklaus.batterytracker.util.validateDecimal /** @@ -62,8 +63,8 @@ class AddBatteryFragment : Fragment() { onValueChange = { value -> model.batteryName = value model.batteryHasError = false - model.batteryHelperId = null - if(batteries.any{ it.name.lowercase() == value.lowercase() }){ + model.batteryHelperId = R.string.helper_required + if(batteries.any{ it.name.equals(value, ignoreCase = true) }){ model.batteryHasError = true model.batteryHelperId = R.string.helper_battery_not_unique } @@ -80,14 +81,10 @@ class AddBatteryFragment : Fragment() { value = model.declaredCapacity, onValueChange = { model.declaredCapacity = validateDecimal(it, model.declaredCapacity) - model.capacityHasError = false - model.capacityHelperId = null }, labelId = R.string.hint_enter_declared_capacity, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), leadingIcon = { Icon(Icons.Default.BatteryFull, "Icon Battery Full") }, - isError = model.capacityHasError, - helperTextId = model.capacityHelperId ) Spacer(Modifier.size(outerPadding)) @@ -95,10 +92,9 @@ class AddBatteryFragment : Fragment() { ExtendedFloatingActionButton( onClick = { if(model.batteryName.isBlank()) model.batteryHasError = true - if(model.declaredCapacity.isBlank()) model.capacityHasError = true - if(!model.batteryHasError && !model.capacityHasError){ - model.saveBattery(model.batteryName, model.declaredCapacity) + if(!model.batteryHasError && model.saveBattery(model.batteryName, model.declaredCapacity)){ + findNavController().navigate(R.id.action_nav_add_battery_to_nav_home) } }, icon = { Icon(Icons.Default.Save, "Icon Save") }, @@ -113,39 +109,6 @@ class AddBatteryFragment : Fragment() { return view } - @Composable - fun MyOutlinedTextField( - value: String, - onValueChange: (String) -> Unit, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - labelId: Int, - leadingIcon: (@Composable () -> Unit)? = null, - isError: Boolean = false, - helperTextId: Int? = null - ) { - val helperTextColor = if(isError){ - MaterialTheme.colors.error - } else { - Gray500 - } - - OutlinedTextField( - value = value, - onValueChange = onValueChange, - keyboardOptions = keyboardOptions, - label = { Text(stringResource(labelId)) }, - leadingIcon = leadingIcon, - isError = isError, - modifier = Modifier.fillMaxWidth() - ) - Text( - text = stringResource(helperTextId ?: R.string.helper_required), - style = MaterialTheme.typography.caption, - color = helperTextColor, - modifier = Modifier.padding(start = 16.dp) - ) - } - /*companion object { * Use this factory method to create a new instance of diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt index 4e21051..61752cf 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt @@ -7,36 +7,42 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.room.BatteryTrackerDB import com.sockenklaus.batterytracker.room.entities.Battery import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.lang.Exception class AddBatteryViewModel(application: Application): AndroidViewModel(application) { + private val app = application private val db = BatteryTrackerDB.getInstance(application) val batteries = db.batteryDao().getBatteries().asLiveData() var batteryName by mutableStateOf("") var batteryHasError by mutableStateOf(false) - var batteryHelperId: Int? by mutableStateOf(null) + var batteryHelperId by mutableStateOf(R.string.helper_required) var declaredCapacity by mutableStateOf("") - var capacityHasError by mutableStateOf(false) - var capacityHelperId: Int? by mutableStateOf(null) fun saveBattery( name: String, - declaredCapacity: String? = null, - ){ + declaredCapacity: String, + ): Boolean { val battery = Battery( name = name, - declaredCapacity = declaredCapacity?.toDouble() + declaredCapacity = if(declaredCapacity.isNotBlank()) declaredCapacity.toDouble() else null ) - viewModelScope.launch(Dispatchers.IO){ - db.batteryDao().insert(battery) + return try { + viewModelScope.launch(Dispatchers.IO){ + db.batteryDao().insert(battery) + } + true + } catch(e: Exception){ + println(e.message) + false } } - } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt index b4f37ce..d0c5e1f 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt @@ -22,11 +22,14 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp +import androidx.navigation.fragment.findNavController import com.google.android.material.datepicker.MaterialDatePicker import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.databinding.FragmentAddChargeBinding import com.sockenklaus.batterytracker.room.entities.Battery +import com.sockenklaus.batterytracker.ui.fragments.composables.MyOutlinedTextField import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme +import com.sockenklaus.batterytracker.ui.theme.Gray500 import com.sockenklaus.batterytracker.util.validateDecimal import java.time.* import java.time.format.DateTimeFormatter @@ -36,7 +39,7 @@ import java.util.* class AddChargeFragment : Fragment() { private var _binding: FragmentAddChargeBinding? = null - private lateinit var viewModel: AddChargeViewModel + private lateinit var model: AddChargeViewModel private val binding get() = _binding!! @OptIn(ExperimentalMaterialApi::class) @@ -47,11 +50,11 @@ class AddChargeFragment : Fragment() { ): View { _binding = FragmentAddChargeBinding.inflate(inflater, container, false) - viewModel = ViewModelProvider(this)[AddChargeViewModel::class.java] - var items = emptyList() + model = ViewModelProvider(this)[AddChargeViewModel::class.java] + var batteries = emptyList() - viewModel.batteries.observe(this.viewLifecycleOwner){ - items = it + model.batteries.observe(this.viewLifecycleOwner){ + batteries = it } binding.root.setContent{ @@ -68,19 +71,26 @@ class AddChargeFragment : Fragment() { expanded = bIdExpanded, onExpandedChange = { bIdExpanded = !bIdExpanded} ) { + + val filteringOptions = batteries.filter { it.name.contains(model.batteryId.text, ignoreCase = true)} OutlinedTextField( - value = viewModel.batteryId, - onValueChange = { viewModel.batteryId = it }, + value = model.batteryId, + onValueChange = { + model.batteryId = it + model.batteryHasError = false + model.batteryHelper = R.string.helper_required + bIdExpanded = filteringOptions.size > 1 + }, label = { Text(stringResource(R.string.select_battery_id)) }, leadingIcon = { Icon(Icons.Default.Tag, null) }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = bIdExpanded) }, colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + isError = model.batteryHasError ) - val filteringOptions = items.filter { it.name.contains(viewModel.batteryId.text, ignoreCase = true)} - if(filteringOptions.size > 1 && viewModel.batteryId.text.isNotBlank()) bIdExpanded = true + if(filteringOptions.isNotEmpty()) { ExposedDropdownMenu( expanded = bIdExpanded, @@ -89,7 +99,7 @@ class AddChargeFragment : Fragment() { for (filteringOption in filteringOptions) { DropdownMenuItem( onClick = { - viewModel.batteryId = TextFieldValue( + model.batteryId = TextFieldValue( text = filteringOption.name, selection = TextRange(filteringOption.name.length) ) @@ -102,30 +112,56 @@ class AddChargeFragment : Fragment() { } } } + Text( + text = stringResource(model.batteryHelper), + style = MaterialTheme.typography.caption, + color = if(model.batteryHasError) MaterialTheme.colors.error else Gray500, + modifier = Modifier.padding(start = 16.dp) + ) Spacer(Modifier.size(innerPadding)) - OutlinedTextField( - value = viewModel.charge, - onValueChange = { viewModel.charge = validateDecimal(it, viewModel.charge) }, - leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") }, + MyOutlinedTextField( + value = model.charge, + onValueChange = { + model.charge = validateDecimal(it, model.charge) + model.chargeHasError = false + }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), - label = { Text(stringResource(R.string.hint_charge)) }, - modifier = Modifier.fillMaxWidth() + labelId = R.string.hint_charge, + leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Chargin Full") }, + isError = model.chargeHasError, + helperTextId = R.string.helper_required ) Spacer(Modifier.size(innerPadding)) ChargeDatePicker( - date = viewModel.date, - onSelect = { viewModel.date = it} + date = model.date, + onSelect = { model.date = it} ) Spacer(Modifier.size(outerPadding)) ExtendedFloatingActionButton( text = { Text(stringResource(R.string.button_save_charge)) }, - onClick = { viewModel.saveCharge() }, + onClick = { + if(!batteries.any{ it.name == model.batteryId.text }){ + model.batteryHasError = true + model.batteryHelper = R.string.helper_battery_not_found + } + if(model.batteryId.text.isBlank()){ + model.batteryHasError = true + model.batteryHelper = R.string.helper_required + } + if(model.charge.isBlank()){ + model.chargeHasError = true + } + + if(!model.batteryHasError && !model.chargeHasError && model.saveCharge(batteries, model.batteryId.text, model.charge, model.date)){ + findNavController().navigate(R.id.action_nav_add_charge_to_nav_home) + } + }, icon = { Icon(Icons.Default.Save, "Icon Save") }, modifier = Modifier.align(Alignment.End) @@ -137,11 +173,6 @@ class AddChargeFragment : Fragment() { return binding.root } - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - @Composable fun ChargeDatePicker( date: LocalDateTime, @@ -174,4 +205,8 @@ class AddChargeFragment : Fragment() { } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt index c337ceb..37904f2 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt @@ -8,10 +8,13 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import androidx.lifecycle.MutableLiveData -import androidx.room.Room +import androidx.lifecycle.viewModelScope +import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.room.BatteryTrackerDB import com.sockenklaus.batterytracker.room.entities.Battery +import com.sockenklaus.batterytracker.room.entities.Charge +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.time.LocalDateTime class AddChargeViewModel(application: Application) : AndroidViewModel(application) { @@ -19,11 +22,43 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio private val db = BatteryTrackerDB.getInstance(application) var batteries: LiveData> = db.batteryDao().getBatteries().asLiveData() - var batteryId: TextFieldValue by mutableStateOf(TextFieldValue("")) + var batteryId by mutableStateOf(TextFieldValue("")) var date: LocalDateTime by mutableStateOf(LocalDateTime.now()) - var charge: String by mutableStateOf("") + var charge by mutableStateOf("") - fun saveCharge() { + var batteryHasError by mutableStateOf(false) + var batteryHelper by mutableStateOf(R.string.helper_required) + var chargeHasError by mutableStateOf(false) + + fun saveCharge( + batteryList: List, + batteryName: String, + charge: String, + date: LocalDateTime + ):Boolean { + + val battery = batteryList.find { it.name == batteryName } + + if (battery != null) { + return try { + viewModelScope.launch(Dispatchers.IO){ + db.chargeDao().insert( + Charge( + batteryId = battery.id, + charge = charge.toDouble(), + date = date + ) + ) + } + true + } catch(e: Exception){ + println(e.message) + false + } + } + + println("Error in saveCharge") + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/composables/Composables.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/composables/Composables.kt new file mode 100644 index 0000000..9bccad9 --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/composables/Composables.kt @@ -0,0 +1,88 @@ +package com.sockenklaus.batterytracker.ui.fragments.composables + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import com.sockenklaus.batterytracker.ui.theme.Gray500 + +@Composable +fun MyOutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + labelId: Int, + leadingIcon: (@Composable () -> Unit)? = null, + trailingIcon: (@Composable () -> Unit)? = null, + isError: Boolean = false, + helperTextId: Int? = null, + colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() +) { + val helperTextColor = if(isError){ + MaterialTheme.colors.error + } else { + Gray500 + } + + OutlinedTextField( + value = value, + onValueChange = onValueChange, + keyboardOptions = keyboardOptions, + label = { Text(stringResource(labelId)) }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + isError = isError, + modifier = Modifier.fillMaxWidth(), + colors = colors + ) + Text( + text = if(helperTextId != null) stringResource(helperTextId) else "", + style = MaterialTheme.typography.caption, + color = helperTextColor, + modifier = Modifier.padding(start = 16.dp) + ) +} + +@Composable +fun MyOutlinedTextField( + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + labelId: Int, + leadingIcon: (@Composable () -> Unit)? = null, + trailingIcon: (@Composable () -> Unit)? = null, + isError: Boolean = false, + helperTextId: Int? = null, + colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() +) { + val helperTextColor = if(isError){ + MaterialTheme.colors.error + } else { + Gray500 + } + Column() { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + keyboardOptions = keyboardOptions, + label = { Text(stringResource(labelId)) }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + isError = isError, + modifier = Modifier.fillMaxWidth(), + colors = colors + ) + Text( + text = if(helperTextId != null) stringResource(helperTextId) else "", + style = MaterialTheme.typography.caption, + color = helperTextColor, + modifier = Modifier.padding(start = 16.dp) + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 7e36753..810535c 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -15,13 +15,21 @@ android:id="@+id/nav_add_charge" android:name="com.sockenklaus.batterytracker.ui.fragments.add_charge.AddChargeFragment" android:label="@string/menu_add_charge" - tools:layout="@layout/fragment_add_charge" /> + tools:layout="@layout/fragment_add_charge" > + + + tools:layout="@layout/fragment_add_battery" > + + AddBattery * Required Battery-Name not unique! + Battery not found \ No newline at end of file