Compare commits

...

10 Commits

Author SHA1 Message Date
Sockenklaus
84c40317a5 Merge HEAD with 6e0eb1c8f4 2022-11-19 15:53:35 +01:00
Sockenklaus
cd1ce2ccda Several changes to implement more streamlined UX. 2022-11-19 00:03:02 +01:00
Sockenklaus
a7c92b020b LiveData to Flow 2022-11-17 10:04:35 +01:00
Sockenklaus
c20d5d55e5 Lots of work on charges list... lots of work to do... 2022-11-17 02:47:33 +01:00
Sockenklaus
49eabce142 Updated dependencies 2022-11-16 14:51:43 +01:00
sockenklaus
6e0eb1c8f4 Implemented some keyboardActions.
Worked on #17
2022-07-25 21:43:23 +02:00
Sockenklaus
0f99fb589c Some work on #17, listeners still missing. 2022-07-23 23:03:17 +02:00
Pascal König
8ca5db7c52 Merge branch 'auto-capitalize' of socrates/batterytracker into master 2022-07-23 16:42:51 +02:00
sockenklaus
3b2e09c134 Implemented auto-capitalization toggle in AddBattery.kt.
Fixed case-sensitive sorting when retrieving batteries
Fixed behaviour of MyOutlinedTextFieldWithSuffix that always used up all the space.
2022-07-23 16:39:45 +02:00
sockenklaus
ebbee65330 Fixed #13 unified outer padding on all components.
Fixed not setting appTitle when navigating via FAB.
Unified "currentScreen" / "appTitle" behaviour.
2022-07-23 15:24:20 +02:00
19 changed files with 498 additions and 350 deletions

17
.idea/deploymentTargetDropDown.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Android\.android\avd\Pixel_3a_API_30_x86.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2022-11-16T20:26:11.531085900Z" />
</component>
</project>

View File

@@ -11,12 +11,12 @@ android {
release { release {
} }
} }
compileSdk 32 compileSdk 33
defaultConfig { defaultConfig {
applicationId "com.sockenklaus.batterytracker" applicationId "com.sockenklaus.batterytracker"
minSdk 29 minSdk 29
targetSdk 32 targetSdk 33
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@@ -41,6 +41,8 @@ android {
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationIdSuffix '.release'
versionNameSuffix 'release'
} }
} }
compileOptions { compileOptions {
@@ -63,50 +65,46 @@ android {
excludes += '/META-INF/{AL2.0,LGPL2.1}' excludes += '/META-INF/{AL2.0,LGPL2.1}'
} }
} }
namespace 'com.sockenklaus.batterytracker'
flavorDimensions
} }
dependencies { dependencies {
implementation 'androidx.room:room-runtime:2.4.2' implementation 'androidx.room:room-runtime:2.4.3'
implementation 'androidx.room:room-rxjava3:2.4.2' implementation 'androidx.room:room-rxjava3:2.4.3'
implementation "androidx.room:room-paging:2.4.2" implementation "androidx.room:room-paging:2.4.3"
implementation "androidx.room:room-ktx:2.4.2" implementation "androidx.room:room-ktx:2.4.3"
kapt 'androidx.room:room-compiler:2.4.2' kapt 'androidx.room:room-compiler:2.4.3'
implementation "androidx.compose.ui:ui:1.3.0-alpha01" implementation "androidx.compose.ui:ui:1.4.0-alpha02"
implementation "androidx.compose.ui:ui-tooling-preview:1.3.0-alpha01" implementation "androidx.compose.ui:ui-tooling-preview:1.4.0-alpha02"
implementation 'androidx.compose.material:material-icons-extended:1.3.0-alpha01' implementation 'androidx.compose.material:material-icons-extended:1.4.0-alpha02'
implementation 'androidx.compose.material:material:1.3.0-alpha01' implementation 'androidx.compose.material:material:1.4.0-alpha02'
implementation 'androidx.compose.runtime:runtime-livedata:1.1.1' implementation 'androidx.compose.runtime:runtime-livedata:1.3.1'
implementation 'androidx.compose.animation:animation:1.3.0-alpha01' implementation 'androidx.compose.animation:animation:1.4.0-alpha02'
implementation 'androidx.compose.ui:ui-tooling:1.3.0-alpha01' implementation 'androidx.compose.ui:ui-tooling:1.4.0-alpha02'
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.3.0-alpha01' androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.4.0-alpha02'
debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.1" debugImplementation "androidx.compose.ui:ui-test-manifest:1.3.1"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0-alpha03'
implementation 'com.google.android.material:compose-theme-adapter:1.1.14'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1" implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0' implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.0' implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation "androidx.navigation:navigation-compose:2.5.0" implementation "androidx.navigation:navigation-compose:2.5.3"
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.activity:activity-compose:1.5.0' implementation 'androidx.activity:activity-compose:1.6.1'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
} }

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
package="com.sockenklaus.batterytracker">
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@@ -16,10 +16,10 @@ interface BatteryDao {
@Update @Update
fun updateBattery(battery: Battery): Int fun updateBattery(battery: Battery): Int
@Query("Select * FROM batteries ORDER BY name ASC") @Query("Select * FROM batteries ORDER BY name COLLATE NOCASE ASC")
fun getBatteries(): Flow<List<Battery>> fun getBatteries(): Flow<List<Battery>>
@Query("Select * FROM batteries WHERE id = :id") @Query("SELECT * FROM batteries WHERE id = :id")
fun getBatteryById(id: Int): Flow<Battery> fun getBatteryById(id: Int): Flow<Battery>
@Transaction @Transaction

View File

@@ -25,4 +25,7 @@ interface ChargeDao {
@Transaction @Transaction
@Query("SELECT * FROM batteries WHERE id = :id") @Query("SELECT * FROM batteries WHERE id = :id")
fun getBatteryAndCharges(id: Int): Flow<BatteryAndCharges> fun getBatteryAndCharges(id: Int): Flow<BatteryAndCharges>
@Query("SELECT * FROM charges WHERE battery_id = :id ORDER BY created_at DESC")
fun getChargesByBatteryId(id: Int): Flow<List<Charge>>
} }

View File

@@ -1,9 +1,9 @@
package com.sockenklaus.batterytracker.ui package com.sockenklaus.batterytracker.ui
import android.annotation.SuppressLint
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -24,36 +24,44 @@ import com.sockenklaus.batterytracker.ui.models.MainViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
object Routes { object Routes {
const val HOME = "home" const val HOME = "Home"
const val ADD_BATTERY = "add_battery" const val ADD_BATTERY = "Add Battery"
const val ADD_CHARGE = "add_charge" const val ADD_CHARGE = "Add Charge"
const val BATTERY_DETAILS = "battery_details" const val BATTERY_DETAILS = "Battery Details"
} }
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable @Composable
fun BatteryTracker() { fun BatteryTracker() {
MaterialTheme { MaterialTheme {
val state: MainViewModel = viewModel() val state: MainViewModel = viewModel()
state.init() state.init()
/**
* TODO Is there a smarter way to work with Scaffold? Is it possible to change the
* components of the Scaffold from within the composables?
*/
Scaffold( Scaffold(
scaffoldState = state.scaffoldState, scaffoldState = state.scaffoldState,
topBar = { topBar = {
when(state.currentScreen){ when {
state.currentScreen.startsWith(Routes.BATTERY_DETAILS) -> {
Routes.BATTERY_DETAILS -> DetailsTopAppBar( DetailsTopAppBar(
navController = state.navController, navController = state.navController,
changeCurrentScreen = { state.currentScreen = it } drawerState = state.scaffoldState.drawerState,
appTitle = state.currentScreen
) )
}
else -> MainTopAppBar( else -> MainTopAppBar(
drawerState = state.scaffoldState.drawerState, drawerState = state.scaffoldState.drawerState,
appTitle = state.appTitle appTitle = state.currentScreen,
) )
} }
} }
) { innerPadding -> ) {
ModalDrawer( ModalDrawer(
drawerState = state.scaffoldState.drawerState, drawerState = state.scaffoldState.drawerState,
drawerContent = { drawerContent = {
@@ -63,38 +71,48 @@ fun BatteryTracker() {
route = Routes.HOME, route = Routes.HOME,
state = state, state = state,
) )
NavListItem(
icon = Icons.Default.BatteryChargingFull,
textId = R.string.nav_add_charge,
route = Routes.ADD_CHARGE,
state = state,
)
NavListItem(
icon = Icons.Default.BatteryFull,
textId = R.string.nav_add_battery,
route = Routes.ADD_BATTERY,
state = state,
)
} }
) { ) {
NavHost( NavHost(
navController = state.navController, navController = state.navController,
startDestination = Routes.HOME, startDestination = Routes.HOME,
modifier = Modifier.padding(innerPadding)) )
{ {
composable(Routes.HOME) { Home(state.navController, state) } composable(Routes.HOME) {
composable(Routes.ADD_CHARGE) { AddCharge(state.navController) } state.currentScreen = Routes.HOME
composable(Routes.ADD_BATTERY) { AddBattery(state.navController) } Home(state.navController)
}
composable(
route = Routes.ADD_CHARGE
){
AddCharge(
navController = state.navController
)
}
composable(
route = "${Routes.ADD_CHARGE}/{batteryId}",
arguments = listOf(navArgument("batteryId"){ type = NavType.IntType })
) {
val id = it.arguments?.getInt("batteryId")
state.currentScreen = "${Routes.ADD_CHARGE} for Battery ${state.getBatteryName(id = id)}"
AddCharge(
batteryId = id,
navController = state.navController
)
}
composable(Routes.ADD_BATTERY) {
state.currentScreen = Routes.ADD_BATTERY
AddBattery(state.navController)
}
composable( composable(
route = "${Routes.BATTERY_DETAILS}/{batteryId}", route = "${Routes.BATTERY_DETAILS}/{batteryId}",
arguments = listOf(navArgument("batteryId"){ type = NavType.IntType }) arguments = listOf(navArgument("batteryId"){ type = NavType.IntType })
) { navBackStackEntry -> ) { navBackStackEntry ->
val id = navBackStackEntry.arguments?.getInt("batteryId")
state.currentScreen = Routes.BATTERY_DETAILS + ": " + state.getBatteryName(id = id)
BatteryDetails( BatteryDetails(
navController = state.navController, batteryId = id ,
batteryId = navBackStackEntry.arguments?.getInt("batteryId"), navController = state.navController
appState = state
) )
} }
} }
@@ -162,9 +180,7 @@ fun NavListItem(
modifier = Modifier modifier = Modifier
.clickable { .clickable {
scope.launch { scope.launch {
state.appTitle = text
state.navController.navigate(route) state.navController.navigate(route)
state.currentScreen = route
state.scaffoldState.drawerState.close() state.scaffoldState.drawerState.close()
} }
} }

View File

@@ -1,6 +1,7 @@
package com.sockenklaus.batterytracker.ui.composables package com.sockenklaus.batterytracker.ui.composables
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -10,11 +11,14 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.ui.Routes
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
import com.sockenklaus.batterytracker.ui.models.AddBatteryViewModel import com.sockenklaus.batterytracker.ui.models.AddBatteryViewModel
import com.sockenklaus.batterytracker.util.validateDecimal import com.sockenklaus.batterytracker.util.validateDecimal
@@ -24,13 +28,18 @@ fun AddBattery(navController: NavController){
val model: AddBatteryViewModel = viewModel() val model: AddBatteryViewModel = viewModel()
val batteries by model.batteries.observeAsState(emptyList()) val batteries by model.batteries.observeAsState(emptyList())
val outerPadding = 24.dp val outerPadding = 16.dp
val innerPadding = 16.dp val innerPadding = 16.dp
Column( Column(
Modifier.padding(outerPadding) Modifier.padding(outerPadding)
) { ) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
){
MyOutlinedTextFieldWithSuffix( MyOutlinedTextFieldWithSuffix(
value = model.batteryName, value = model.batteryName,
onValueChange = { value -> onValueChange = { value ->
@@ -42,11 +51,32 @@ fun AddBattery(navController: NavController){
model.batteryHelperId = R.string.helper_battery_not_unique model.batteryHelperId = R.string.helper_battery_not_unique
} }
}, },
singleLine = true,
labelId = R.string.hint_enter_battery_name, labelId = R.string.hint_enter_battery_name,
leadingIcon = { Icon(Icons.Default.Tag, "Icon Tag") }, leadingIcon = { Icon(Icons.Default.Tag, "Icon Tag") },
isError = model.batteryHasError, isError = model.batteryHasError,
helperTextId = model.batteryHelperId helperTextId = model.batteryHelperId,
keyboardOptions = KeyboardOptions(
capitalization = if(model.switchAutoCap) KeyboardCapitalization.Characters else KeyboardCapitalization.None,
autoCorrect = false,
keyboardType = KeyboardType.Ascii,
imeAction = ImeAction.Next
) )
)
Column() {
Switch(
checked = model.switchAutoCap,
onCheckedChange = { model.switchAutoCap = !model.switchAutoCap }
)
Text(
text = "Auto-Cap.",
style = MaterialTheme.typography.caption
)
}
}
Spacer(Modifier.size(innerPadding)) Spacer(Modifier.size(innerPadding))
@@ -56,24 +86,38 @@ fun AddBattery(navController: NavController){
model.declaredCapacity = validateDecimal(it, model.declaredCapacity) model.declaredCapacity = validateDecimal(it, model.declaredCapacity)
}, },
labelId = R.string.hint_enter_declared_capacity, labelId = R.string.hint_enter_declared_capacity,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Decimal,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
saveBattery(model = model, navController = navController)
}
),
leadingIcon = { Icon(Icons.Default.BatteryFull, "Icon Battery Full") }, leadingIcon = { Icon(Icons.Default.BatteryFull, "Icon Battery Full") },
suffix = "Ah" suffix = "Ah",
modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.size(outerPadding)) Spacer(Modifier.size(outerPadding))
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
onClick = { onClick = { saveBattery(model = model, navController = navController) },
if(model.batteryName.isBlank()) model.batteryHasError = true
if(!model.batteryHasError && model.saveBattery(model.batteryName, model.declaredCapacity)){
navController.navigate("home")
}
},
icon = { Icon(Icons.Default.Save, "Icon Save") }, icon = { Icon(Icons.Default.Save, "Icon Save") },
text = { Text(stringResource(R.string.button_save_battery)) }, text = { Text(stringResource(R.string.button_save_battery)) },
modifier = Modifier.align(Alignment.End) modifier = Modifier.align(Alignment.End)
) )
} }
} }
private fun saveBattery(
model: AddBatteryViewModel,
navController: NavController
){
if(model.batteryName.isBlank()) model.batteryHasError = true
if(!model.batteryHasError && model.saveBattery(model.batteryName, model.declaredCapacity)) {
navController.navigate(Routes.HOME)
}
}

View File

@@ -2,6 +2,7 @@ package com.sockenklaus.batterytracker.ui.composables
import android.app.DatePickerDialog import android.app.DatePickerDialog
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -16,12 +17,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.ui.Routes
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
import com.sockenklaus.batterytracker.ui.models.AddChargeViewModel import com.sockenklaus.batterytracker.ui.models.AddChargeViewModel
import com.sockenklaus.batterytracker.ui.theme.Gray500 import com.sockenklaus.batterytracker.ui.theme.Gray500
@@ -33,14 +37,23 @@ import java.util.*
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun AddCharge(navController: NavController){ fun AddCharge(
val outerPadding = 24.dp batteryId: Int? = null,
navController: NavController
){
val outerPadding = 16.dp
val innerPadding = 16.dp val innerPadding = 16.dp
var bIdExpanded by remember { mutableStateOf(false)} var bIdExpanded by remember { mutableStateOf(false)}
val model: AddChargeViewModel = viewModel() val model: AddChargeViewModel = viewModel()
val batteries by model.batteries.observeAsState(emptyList()) val batteries by model.batteries.observeAsState(emptyList())
if(batteryId != null && batteries.any { it.id == batteryId }) {
model.batteryName = TextFieldValue(
text = batteries.find { it.id == batteryId }!!.name
)
}
Column( Column(
Modifier.padding(outerPadding) Modifier.padding(outerPadding)
) { ) {
@@ -49,11 +62,12 @@ fun AddCharge(navController: NavController){
onExpandedChange = { bIdExpanded = !bIdExpanded} onExpandedChange = { bIdExpanded = !bIdExpanded}
) { ) {
val filteringOptions = batteries.filter { it.name.contains(model.batteryId.text, ignoreCase = true)} val filteringOptions = batteries.filter { it.name.contains(model.batteryName.text, ignoreCase = true)}
OutlinedTextField( OutlinedTextField(
value = model.batteryId, enabled = batteryId == null,
value = model.batteryName,
onValueChange = { onValueChange = {
model.batteryId = it model.batteryName = it
model.batteryHasError = false model.batteryHasError = false
model.batteryHelper = R.string.helper_required model.batteryHelper = R.string.helper_required
bIdExpanded = filteringOptions.size > 1 bIdExpanded = filteringOptions.size > 1
@@ -65,7 +79,17 @@ fun AddCharge(navController: NavController){
}, },
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
isError = model.batteryHasError isError = model.batteryHasError,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = {
bIdExpanded = false
defaultKeyboardAction(ImeAction.Next)
}
),
singleLine = true
) )
if(filteringOptions.isNotEmpty()) { if(filteringOptions.isNotEmpty()) {
@@ -76,7 +100,7 @@ fun AddCharge(navController: NavController){
for (filteringOption in filteringOptions) { for (filteringOption in filteringOptions) {
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
model.batteryId = TextFieldValue( model.batteryName = TextFieldValue(
text = filteringOption.name, text = filteringOption.name,
selection = TextRange(filteringOption.name.length) selection = TextRange(filteringOption.name.length)
) )
@@ -104,12 +128,21 @@ fun AddCharge(navController: NavController){
model.charge = validateDecimal(it, model.charge) model.charge = validateDecimal(it, model.charge)
model.chargeHasError = false model.chargeHasError = false
}, },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Decimal,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
saveCharge(batteries, model, navController)
}
),
labelId = R.string.hint_charge, labelId = R.string.hint_charge,
leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") }, leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") },
isError = model.chargeHasError, isError = model.chargeHasError,
helperTextId = R.string.helper_required, helperTextId = R.string.helper_required,
suffix = "Ah" suffix = "Ah",
modifier = Modifier.fillMaxWidth(),
) )
Spacer(Modifier.size(innerPadding)) Spacer(Modifier.size(innerPadding))
@@ -124,21 +157,18 @@ fun AddCharge(navController: NavController){
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.button_save_charge)) }, text = { Text(stringResource(R.string.button_save_charge)) },
onClick = { onClick = {
if(!batteries.any{ it.name == model.batteryId.text }){ if(!batteries.any{ it.name == model.batteryName.text }){
model.batteryHasError = true model.batteryHasError = true
model.batteryHelper = R.string.helper_battery_not_found model.batteryHelper = R.string.helper_battery_not_found
} }
if(model.batteryId.text.isBlank()){ if(model.batteryName.text.isBlank()){
model.batteryHasError = true model.batteryHasError = true
model.batteryHelper = R.string.helper_required model.batteryHelper = R.string.helper_required
} }
if(model.charge.isBlank()){ if(model.charge.isBlank()){
model.chargeHasError = true model.chargeHasError = true
} }
saveCharge(batteries, model, navController)
if(!model.batteryHasError && !model.chargeHasError && model.saveCharge(batteries, model.batteryId.text, model.charge, model.date)){
navController.navigate("home")
}
}, },
icon = { Icon(Icons.Default.Save, "Icon Save") }, icon = { Icon(Icons.Default.Save, "Icon Save") },
modifier = Modifier.align(Alignment.End) modifier = Modifier.align(Alignment.End)
@@ -147,6 +177,31 @@ fun AddCharge(navController: NavController){
} }
} }
fun saveCharge(
batteries: List<Battery>,
model: AddChargeViewModel,
navController: NavController
) {
if(!batteries.any{ it.name == model.batteryName.text }){
model.batteryHasError = true
model.batteryHelper = R.string.helper_battery_not_found
}
if(model.batteryName.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.batteryName.text, model.charge, model.date)){
val id = model.getBatId(
batteries,
model.batteryName.text
)
navController.navigate("${Routes.BATTERY_DETAILS}/${id}")
}
}
@Composable @Composable
fun ChargeDatePicker( fun ChargeDatePicker(

View File

@@ -1,77 +1,94 @@
package com.sockenklaus.batterytracker.ui.composables package com.sockenklaus.batterytracker.ui.composables
import androidx.activity.OnBackPressedCallback import androidx.compose.foundation.layout.*
import androidx.activity.OnBackPressedDispatcher import androidx.compose.foundation.lazy.LazyColumn
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.Icon import androidx.compose.foundation.lazy.items
import androidx.compose.material.IconButton import androidx.compose.material.*
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.sockenklaus.batterytracker.R
import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.room.entities.Charge
import com.sockenklaus.batterytracker.ui.AppBarTitle
import com.sockenklaus.batterytracker.ui.Routes import com.sockenklaus.batterytracker.ui.Routes
import com.sockenklaus.batterytracker.ui.models.MainViewModel import com.sockenklaus.batterytracker.ui.models.BatteryDetailsViewModel
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.*
@OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun BatteryDetails( fun BatteryDetails(
navController: NavController, batteryId: Int?,
batteryId: Int? = null, navController: NavController
appState: MainViewModel
){ ){
var onBack = { val model: BatteryDetailsViewModel = viewModel()
val backTarget = navController.previousBackStackEntry?.destination?.route ?: Routes.HOME val battery by model.battery.collectAsState(Battery(name = ""))
appState.currentScreen = backTarget val charges: List<Charge> by model.charges.collectAsState(emptyList())
navController.navigate(backTarget)
Box(
modifier = Modifier
.fillMaxSize()
){
val outputFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.GERMANY)
LazyColumn(
state = LazyListState()
) {
items(charges){ charge ->
ListItem(
text = { Text("Charge: " + charge.charge.toString() + " Ah") },
secondaryText = { Text("Date: " + charge.date.format(outputFormat)) }
)
Divider(Modifier.padding(horizontal = 16.dp))
}
}
ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.add_charge)) },
onClick = {
navController.navigate("${Routes.ADD_CHARGE}/${battery.id}")
},
icon = { Icon(Icons.Default.Add, "Icon Add") },
modifier = Modifier.align(Alignment.BottomEnd)
.padding(16.dp)
)
} }
BackPressHandler(onBackPressed = onBack)
Text(batteryId.toString())
} }
@OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun DetailsTopAppBar( fun DetailsTopAppBar(
navController: NavController, navController: NavController,
changeCurrentScreen: (String) -> Unit appTitle: String,
drawerState: DrawerState
){ ){
TopAppBar( TopAppBar(
title = { Text("Details") }, title = {
AppBarTitle(
drawerTarget = drawerState.targetValue,
appTitle = appTitle
)
},
navigationIcon = { navigationIcon = {
IconButton( IconButton(
onClick = { onClick = {
navController.navigate("home") navController.navigate(Routes.HOME)
changeCurrentScreen(Routes.HOME)
} }
) { ) {
Icon(Icons.Default.ArrowBack, null) Icon(Icons.Default.ArrowBack, null)
} }
} },
) )
} }
@Composable
fun BackPressHandler(
backPressedDispatcher: OnBackPressedDispatcher? = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher,
onBackPressed: () -> Unit
) {
val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed)
val backCallback = remember {
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
currentOnBackPressed()
}
}
}
DisposableEffect(key1 = backPressedDispatcher) {
backPressedDispatcher?.addCallback(backCallback)
onDispose {
backCallback.remove()
}
}
}

View File

@@ -1,17 +1,22 @@
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.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
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
@@ -20,36 +25,50 @@ import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.ui.Routes 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
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun Home( fun Home(
navController: NavController, 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("")}
val filteredList = batteries.filter { it.name.contains(filterText, ignoreCase = true) } val filteredList = batteries.filter { it.name.contains(filterText, ignoreCase = true) }
val modHorizontalPadding = Modifier.padding(horizontal = 16.dp)
Column { Box(
modifier = Modifier.fillMaxSize()
){
Column(
modifier = Modifier
.padding(bottom = 16.dp)
.fillMaxHeight()
) {
MyOutlinedTextFieldWithSuffix( MyOutlinedTextFieldWithSuffix(
value = filterText, value = filterText,
onValueChange = { filterText = it }, onValueChange = { filterText = it },
labelId = R.string.hint_filter_batteries, labelId = R.string.hint_filter_batteries,
modifier = Modifier.padding( modifier = Modifier
.padding(
top = 16.dp,
start = 16.dp, start = 16.dp,
end = 16.dp, end = 16.dp
top = 16.dp )
.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search
),
keyboardActions = KeyboardActions(
onSearch = {
}
) )
) )
LazyColumn( LazyColumn(
state = LazyListState() state = LazyListState(),
) { ) {
items(filteredList){ battery -> items(filteredList){ battery ->
ListItem( ListItem(
@@ -65,13 +84,22 @@ fun Home(
}, },
modifier = Modifier.clickable { modifier = Modifier.clickable {
navController.navigate("${Routes.BATTERY_DETAILS}/${battery.id}") navController.navigate("${Routes.BATTERY_DETAILS}/${battery.id}")
appState.currentScreen = Routes.BATTERY_DETAILS
} }
) )
Divider(modHorizontalPadding) Divider(Modifier.padding(horizontal = 16.dp))
} }
} }
} }
ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.nav_add_battery)) },
icon = { Icon(Icons.Default.Add, contentDescription = "Add Battery") },
onClick = {
navController.navigate(Routes.ADD_BATTERY)
},
modifier = Modifier.padding(16.dp)
.align(Alignment.BottomEnd)
)
}
} }
@Composable @Composable

View File

@@ -21,90 +21,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sockenklaus.batterytracker.ui.theme.Gray500 import com.sockenklaus.batterytracker.ui.theme.Gray500
/*@Composable
fun TopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable (() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit = {},
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = AppBarDefaults.TopAppBarElevation
) {
AppBar(
backgroundColor,
contentColor,
elevation,
AppBarDefaults.ContentPadding,
RectangleShape,
modifier
) {
if (navigationIcon == null) {
Spacer(Modifier.width(16.dp - 4.dp))
} else {
Row(Modifier.fillMaxHeight().width(72.dp - 4.dp), verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
content = navigationIcon
)
}
}
Row(
Modifier.fillMaxHeight().weight(1f),
verticalAlignment = Alignment.CenterVertically
) {
ProvideTextStyle(value = MaterialTheme.typography.h6) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
content = title
)
}
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
Modifier.fillMaxHeight(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
}
}
@Composable
private fun AppBar(
backgroundColor: Color,
contentColor: Color,
elevation: Dp,
contentPadding: PaddingValues,
shape: Shape,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Surface(
color = backgroundColor,
contentColor = contentColor,
elevation = elevation,
shape = shape,
modifier = modifier
) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
Modifier.fillMaxWidth()
.padding(contentPadding)
.height(56.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}*/
@Composable @Composable
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
fun MyOutlinedTextFieldWithSuffix( fun MyOutlinedTextFieldWithSuffix(
@@ -142,11 +58,11 @@ fun MyOutlinedTextFieldWithSuffix(
Gray500 Gray500
} }
Column() {
BasicTextField( BasicTextField(
value = value, value = value,
modifier = modifier modifier = modifier
.padding(top = 8.dp) .padding(top = 8.dp)
.background(colors.backgroundColor(enabled).value, shape) .background(colors.backgroundColor(enabled).value, shape)
.defaultMinSize( .defaultMinSize(
minWidth = TextFieldDefaults.MinWidth, minWidth = TextFieldDefaults.MinWidth,
@@ -168,9 +84,11 @@ fun MyOutlinedTextFieldWithSuffix(
value = value, value = value,
visualTransformation = visualTransformation, visualTransformation = visualTransformation,
innerTextField = { innerTextField = {
Row { Row(
horizontalArrangement = Arrangement.SpaceBetween
) {
val alignModifier = Modifier.alignByBaseline() val alignModifier = Modifier.alignByBaseline()
Box(alignModifier.weight(1f)){ Box(alignModifier){
innerTextField() innerTextField()
} }
Text( Text(
@@ -207,4 +125,5 @@ fun MyOutlinedTextFieldWithSuffix(
color = helperTextColor, color = helperTextColor,
modifier = Modifier.padding(start = 16.dp) modifier = Modifier.padding(start = 16.dp)
) )
}
} }

View File

@@ -22,6 +22,7 @@ class AddBatteryViewModel(application: Application): AndroidViewModel(applicatio
var batteryName by mutableStateOf("") var batteryName by mutableStateOf("")
var batteryHasError by mutableStateOf(false) var batteryHasError by mutableStateOf(false)
var batteryHelperId by mutableStateOf(R.string.helper_required) var batteryHelperId by mutableStateOf(R.string.helper_required)
var switchAutoCap by mutableStateOf(true)
var declaredCapacity by mutableStateOf("") var declaredCapacity by mutableStateOf("")

View File

@@ -1,7 +1,6 @@
package com.sockenklaus.batterytracker.ui.models package com.sockenklaus.batterytracker.ui.models
import android.app.Application import android.app.Application
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -23,7 +22,7 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio
private val db = BatteryTrackerDB.getInstance(application) private val db = BatteryTrackerDB.getInstance(application)
var batteries: LiveData<List<Battery>> = db.batteryDao().getBatteries().asLiveData() var batteries: LiveData<List<Battery>> = db.batteryDao().getBatteries().asLiveData()
var batteryId by mutableStateOf(TextFieldValue("")) var batteryName by mutableStateOf(TextFieldValue(""))
var date: LocalDate by mutableStateOf(LocalDate.now()) var date: LocalDate by mutableStateOf(LocalDate.now())
var charge by mutableStateOf("") var charge by mutableStateOf("")
@@ -32,6 +31,14 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio
var chargeHasError by mutableStateOf(false) var chargeHasError by mutableStateOf(false)
fun getBatId(
batteries: List<Battery>,
name: String
) : Int? {
return batteries.find { it.name == name }?.id
}
fun saveCharge( fun saveCharge(
batteryList: List<Battery>, batteryList: List<Battery>,
batteryName: String, batteryName: String,

View File

@@ -0,0 +1,29 @@
package com.sockenklaus.batterytracker.ui.models
import android.app.Application
import androidx.lifecycle.*
import com.sockenklaus.batterytracker.room.BatteryTrackerDB
import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.room.entities.Charge
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class BatteryDetailsViewModel(
application: Application,
state: SavedStateHandle
) : AndroidViewModel(application) {
private val db = BatteryTrackerDB.getInstance(application)
val battery: Flow<Battery> = if(state.get<Int?>("batteryId") !== null) {
db.batteryDao().getBatteryById(state["batteryId"]!!)
} else {
MutableStateFlow(Battery(name=""))
}
val charges: Flow<List<Charge>> = if(state.get<Int?>("batteryId") !== null){
db.chargeDao().getChargesByBatteryId(state["batteryId"]!!)
} else {
MutableStateFlow(emptyList())
}
}

View File

@@ -21,7 +21,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
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.minOfOrNull { it.charge }
} }
return null return null
} }
@@ -32,7 +32,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
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.maxOfOrNull { it.charge }
} }
return null return null
} }

View File

@@ -2,17 +2,16 @@ 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.compose.ui.platform.LocalContext
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.room.BatteryTrackerDB
import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.ui.Routes import com.sockenklaus.batterytracker.ui.Routes
import com.sockenklaus.batterytracker.ui.ToggleDrawerButton
class MainViewModel : ViewModel() { class MainViewModel : ViewModel() {
var appTitle by mutableStateOf("Home")
var currentScreen by mutableStateOf(Routes.HOME) var currentScreen by mutableStateOf(Routes.HOME)
lateinit var navController: NavHostController lateinit var navController: NavHostController
@@ -23,4 +22,20 @@ class MainViewModel : ViewModel() {
navController = rememberNavController() navController = rememberNavController()
scaffoldState = rememberScaffoldState() scaffoldState = rememberScaffoldState()
} }
@Composable
fun getBatteryName(
id: Int?
) : String {
val db = BatteryTrackerDB.getInstance(LocalContext.current)
val battery = id?.let { db.batteryDao().getBatteryById(it).collectAsState(initial = Battery(name="")) }
var name = ""
if (battery != null){
name = battery.value.name
}
return name
}
} }

View File

@@ -11,7 +11,7 @@
<string name="nav_header_title">Navigation</string> <string name="nav_header_title">Navigation</string>
<string name="nav_home">Home</string> <string name="nav_home">Home</string>
<string name="nav_add_charge">Add Charge</string> <string name="add_charge">Add Charge</string>
<string name="nav_add_battery">Add Battery</string> <string name="nav_add_battery">Add Battery</string>
<string name="battery_id">Battery ID</string> <string name="battery_id">Battery ID</string>

View File

@@ -5,8 +5,8 @@ ext {
} }
}// Top-level build file where you can add configuration options common to all sub-projects/modules. }// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '7.2.1' apply false id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.2.1' apply false id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
id 'org.jetbrains.kotlin.kapt' version '1.7.0' apply false id 'org.jetbrains.kotlin.kapt' version '1.7.0' apply false
} }

View File

@@ -1,6 +1,6 @@
#Sun Jul 10 23:29:55 CEST 2022 #Sun Jul 10 23:29:55 CEST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME