Implemented AddChargeFragment.kt functionality
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Battery>()
|
||||
model = ViewModelProvider(this)[AddChargeViewModel::class.java]
|
||||
var batteries = emptyList<Battery>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<List<Battery>> = 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<Battery>,
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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" >
|
||||
<action
|
||||
android:id="@+id/action_nav_add_charge_to_nav_home"
|
||||
app:destination="@id/nav_home" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_add_battery"
|
||||
android:name="com.sockenklaus.batterytracker.ui.fragments.add_battery.AddBatteryFragment"
|
||||
android:label="@string/menu_add_battery"
|
||||
tools:layout="@layout/fragment_add_battery" />
|
||||
tools:layout="@layout/fragment_add_battery" >
|
||||
<action
|
||||
android:id="@+id/action_nav_add_battery_to_nav_home"
|
||||
app:destination="@id/nav_home" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_settings"
|
||||
|
||||
@@ -29,4 +29,5 @@
|
||||
<string name="title_activity_add_battery">AddBattery</string>
|
||||
<string name="helper_required">* Required</string>
|
||||
<string name="helper_battery_not_unique">Battery-Name not unique!</string>
|
||||
<string name="helper_battery_not_found">Battery not found</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user