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 d6c3897edf03ce37e324992523c904242adc82c2..f6a2502e9ea3b7562714a8251232ebfb75552f5b 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 266a4208ee52deb290d5bdd4dbbede74b346b438..32e4ebbafb9f77ecc65717551ee609b903c401a9 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 5b6f379dba3202f12fa5be87e31c3fe223373994..da2ae25104559c75dad420489969e3f25e5bbbf4 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 f172270b6e9e9db2cd36985e3f0a3f8b7099d848..2904b87780f9b8db0ad0d431fd48282457fb5927 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) }