From 073f54aac7369dfa85a14d8b8e24a6db0ac62a5c Mon Sep 17 00:00:00 2001 From: Iftakhar Husan <AC5636@student.jamk.fi> Date: Sat, 6 Jan 2024 02:12:04 +0200 Subject: [PATCH] Minor adjustment to input validation and initial message showing delay --- .../e13roomshoppinglist/MainActivity.kt | 34 ++++++++++++++++- .../ShoppingListDatabase.kt | 2 +- .../e13roomshoppinglist/ShoppingListScreen.kt | 37 +++++++++---------- .../ShoppingListViewModel.kt | 31 ++++++++++------ 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/MainActivity.kt b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/MainActivity.kt index d6c3897..f6a2502 100644 --- a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/MainActivity.kt +++ b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/MainActivity.kt @@ -1,5 +1,6 @@ package com.example.e13roomshoppinglist +import android.annotation.SuppressLint import android.os.Bundle import android.widget.Toast import androidx.activity.ComponentActivity @@ -12,6 +13,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -19,6 +21,10 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.room.Room import com.example.e13roomshoppinglist.ui.theme.E13RoomShoppingListTheme +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private val db by lazy { @@ -36,6 +42,8 @@ class MainActivity : ComponentActivity() { } } + @OptIn(DelicateCoroutinesApi::class) + @SuppressLint("CoroutineCreationDuringComposition") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { @@ -43,10 +51,14 @@ class MainActivity : ComponentActivity() { val state by viewModel.state.collectAsState() val ctx = LocalContext.current var toast by remember { - mutableStateOf<Toast?>(Toast.makeText(ctx, "Loaded data from room db!", Toast.LENGTH_SHORT)) + mutableStateOf<Toast?>(null) + } + var isDataLoadMsgShown by rememberSaveable { + mutableStateOf(false) } val showToast = { msg: String -> + toast?.cancel() toast = Toast.makeText(ctx, msg, Toast.LENGTH_SHORT) toast?.show() } @@ -55,7 +67,25 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - ShoppingListScreen(state = state, onEvent = viewModel::onEvent, onShowToast = showToast) + ShoppingListScreen( + state = state, + onEvent = viewModel::onEvent, + onShowToast = showToast + ) + if (!isDataLoadMsgShown) { + GlobalScope.launch { + delay(2000) + runOnUiThread { + val itemCount = state.shoppingListItems.count() + if (itemCount == 0) { + showToast("Database is empty!") + } else { + showToast("Loaded $itemCount item${if (itemCount==1) "" else "s"} from database!") + } + isDataLoadMsgShown = true + } + } + } } } } diff --git a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListDatabase.kt b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListDatabase.kt index 266a420..32e4ebb 100644 --- a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListDatabase.kt +++ b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListDatabase.kt @@ -38,7 +38,7 @@ data class ShoppingListItem( @Dao interface ShoppingListDao{ @Upsert - fun upsertShoppingListItem(shoppingListItem: ShoppingListItem) + suspend fun upsertShoppingListItem(shoppingListItem: ShoppingListItem) @Delete suspend fun deleteShoppingListItem(shoppingListItem: ShoppingListItem) diff --git a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListScreen.kt b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListScreen.kt index 5b6f379..da2ae25 100644 --- a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListScreen.kt +++ b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListScreen.kt @@ -1,7 +1,6 @@ package com.example.e13roomshoppinglist -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement @@ -10,7 +9,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn @@ -124,6 +122,7 @@ fun ShoppingListScreen( ) } } + Divider(thickness = 1.dp, color = Color.LightGray) } item { Row( @@ -132,7 +131,7 @@ fun ShoppingListScreen( .height(70.dp), verticalAlignment = Alignment.CenterVertically ) { - Text(text = "Empty item to fill up room so that the last item does not go under FAB.", fontSize = 12.sp, color = Color.Gray) + Text(text = "Empty item to fill up room so that the last item's edit and delete button does not go under FAB.", fontSize = 12.sp, color = Color.Gray) } } } @@ -168,35 +167,26 @@ fun AddShoppingListItemDialog( placeholder = { Text(text = "Item Name") }, ) TextField( - value = if(shoppingListItem.count>0) shoppingListItem.count.toString() else "", + value = shoppingListItem.count, label = { Text(text = "Count") }, placeholder = { Text(text = "Count") }, onValueChange = { - var count = it.toIntOrNull() - if (count===null) { - onShowToast("Invalid value for Count!") - count = 0 - } - onEvent(ShoppingListEvent.SetCount(count = count)) + onEvent(ShoppingListEvent.SetCount(count = it)) }, keyboardOptions = KeyboardOptions().copy( autoCorrect = false, - keyboardType = KeyboardType.Number + keyboardType = KeyboardType.NumberPassword ), leadingIcon = { Text(text = "🧮", fontSize = 24.sp) } ) TextField( - value = if(shoppingListItem.price>0) shoppingListItem.price.toString() else "", + value = shoppingListItem.price, label = { Text(text = "Price") }, placeholder = { Text(text = "Price") }, onValueChange = { - var price = it.toFloatOrNull() - if (price===null) { - price = 0f - } - onEvent(ShoppingListEvent.SetPrice(price = price)) + onEvent(ShoppingListEvent.SetPrice(price = it)) }, keyboardOptions = KeyboardOptions().copy( autoCorrect = false, @@ -211,13 +201,22 @@ fun AddShoppingListItemDialog( confirmButton = { Button(onClick = { if (state.newShoppingListItem.name.length<3){ - onShowToast("Item name should not be too short!") + onShowToast("Item Name is too short!") + return@Button + } + if (state.newShoppingListItem.count.toIntOrNull()===null){ + onShowToast("Invalid value for Count! Failed to convert value to Int.") return@Button } + if (state.newShoppingListItem.price.toFloatOrNull()===null){ + onShowToast("Invalid value for Price! Failed to convert value to Float.") + return@Button + } + if (state.isAddingNewShoppingListItem) { onEvent(ShoppingListEvent.InsertNewShoppingListItemToDb).also { onShowToast("Added new item to shopping list!") } } else if (state.isEditingShoppingListItem) { - onEvent(ShoppingListEvent.UpdateCurrentlyEditingShoppingListItem).also { onShowToast("Updated shopping list item!") } + onEvent(ShoppingListEvent.UpdateCurrentlyEditingShoppingListItem).also { onShowToast("Updated shopping list item ${state.currentlyEditingShoppingListItem?.name}!") } } }) { Text(text = if (state.isEditingShoppingListItem) "Update" else "Insert") diff --git a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListViewModel.kt b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListViewModel.kt index f172270..2904b87 100644 --- a/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListViewModel.kt +++ b/E13RoomShoppingList/app/src/main/java/com/example/e13roomshoppinglist/ShoppingListViewModel.kt @@ -28,8 +28,8 @@ sealed interface ShoppingListEvent{ data object OpenAddingNewShoppingListItemDialog: ShoppingListEvent data object CloseAddingNewShoppingListItemDialog: ShoppingListEvent data class SetName(val name: String): ShoppingListEvent - data class SetPrice(val price: Float): ShoppingListEvent - data class SetCount(val count: Int): ShoppingListEvent + data class SetPrice(val price: String): ShoppingListEvent + data class SetCount(val count: String): ShoppingListEvent data object InsertNewShoppingListItemToDb: ShoppingListEvent data class OpenEditingShoppingListItem(val shoppingListItem: ShoppingListItem): ShoppingListEvent @@ -42,8 +42,8 @@ sealed interface ShoppingListEvent{ data class NewShoppingListItem( val name: String = "", - val price: Float = 0f, - val count: Int = 0 + val price: String = "", + val count: String = "" ) data class ShoppingListState( @@ -103,12 +103,16 @@ class ShoppingListViewModel(private val dao: ShoppingListDao): ViewModel() { ShoppingListEvent.InsertNewShoppingListItemToDb -> { val newShoppingListItem = _state.value.newShoppingListItem GlobalScope.launch { + val price = newShoppingListItem.price.toFloatOrNull() + val count = newShoppingListItem.count.toIntOrNull() + if (price===null || count===null) return@launch + dao.upsertShoppingListItem(ShoppingListItem( name = newShoppingListItem.name, - price = newShoppingListItem.price, - count = newShoppingListItem.count)) + price = price, + count = count)) } - _state.update { it.copy(newShoppingListItem=NewShoppingListItem()) } + _state.update { it.copy(newShoppingListItem= NewShoppingListItem()) } } ShoppingListEvent.CloseAddingNewShoppingListItemDialog -> { _state.update { it.copy(isAddingNewShoppingListItem = false) } @@ -117,13 +121,14 @@ class ShoppingListViewModel(private val dao: ShoppingListDao): ViewModel() { is ShoppingListEvent.OpenEditingShoppingListItem -> { _state.update { val shoppingListItem = event.shoppingListItem + it.copy( isEditingShoppingListItem = true, currentlyEditingShoppingListItem = shoppingListItem, newShoppingListItem = NewShoppingListItem( name = shoppingListItem.name, - price = shoppingListItem.price, - count = shoppingListItem.count + price = shoppingListItem.price.toString(), + count = shoppingListItem.count.toString() )) } } @@ -131,10 +136,14 @@ class ShoppingListViewModel(private val dao: ShoppingListDao): ViewModel() { val currentlyEditingShoppingListItem = _state.value.currentlyEditingShoppingListItem if (currentlyEditingShoppingListItem===null) return val modifiedShoppingListItemAttrs = _state.value.newShoppingListItem + val price = modifiedShoppingListItemAttrs.price.toFloatOrNull() + val count = modifiedShoppingListItemAttrs.count.toIntOrNull() + if (price===null || count===null) return + val updatedShoppingListItem = currentlyEditingShoppingListItem.copy( name = modifiedShoppingListItemAttrs.name, - price = modifiedShoppingListItemAttrs.price, - count = modifiedShoppingListItemAttrs.count + price = price, + count = count ) GlobalScope.launch { dao.upsertShoppingListItem(updatedShoppingListItem) } -- GitLab