Skip to content
Snippets Groups Projects
Unverified Commit c901c0e4 authored by AC5636's avatar AC5636 :ghost:
Browse files

Finish exercise 13

parent f5eee4a2
No related branches found
No related tags found
No related merge requests found
Showing
with 848 additions and 0 deletions
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
E13 Room Shopping List
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp") version "1.9.22-1.0.16"
}
android {
namespace = "com.example.e13roomshoppinglist"
compileSdk = 34
defaultConfig {
applicationId = "com.example.e13roomshoppinglist"
minSdk = 31
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
// Room
val roomVersion = "2.6.1"
implementation("androidx.room:room-ktx:$roomVersion")
ksp("androidx.room:room-compiler:$roomVersion")
// Material compose 3
implementation("androidx.compose.material3:material3:1.1.2")
implementation("androidx.compose.material3:material3-window-size-class:1.1.2")
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.example.e13roomshoppinglist
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.e13roomshoppinglist", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.E13RoomShoppingList"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.E13RoomShoppingList">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package com.example.e13roomshoppinglist
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.room.Room
import com.example.e13roomshoppinglist.ui.theme.E13RoomShoppingListTheme
class MainActivity : ComponentActivity() {
private val db by lazy {
Room.databaseBuilder(
applicationContext,
ShoppingListDatabase::class.java,
"shoppingListDB.db"
).build()
}
private val viewModel by viewModels<ShoppingListViewModel> {
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ShoppingListViewModel(db.dao) as T
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
E13RoomShoppingListTheme {
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))
}
val showToast = { msg: String ->
toast = Toast.makeText(ctx, msg, Toast.LENGTH_SHORT)
toast?.show()
}
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ShoppingListScreen(state = state, onEvent = viewModel::onEvent, onShowToast = showToast)
}
}
}
}
}
package com.example.e13roomshoppinglist
import androidx.room.ColumnInfo
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Delete
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.RoomDatabase
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Database(
entities = [ShoppingListItem::class],
version = 1
)
abstract class ShoppingListDatabase:RoomDatabase() {
abstract val dao: ShoppingListDao
}
@Entity
data class ShoppingListItem(
@PrimaryKey(autoGenerate = true)
val id: Int? = null,
@ColumnInfo(defaultValue = "")
val name: String,
@ColumnInfo(defaultValue = 0.toString())
val price: Float,
@ColumnInfo(defaultValue = 0.toString())
val count: Int,
)
@Dao
interface ShoppingListDao{
@Upsert
fun upsertShoppingListItem(shoppingListItem: ShoppingListItem)
@Delete
suspend fun deleteShoppingListItem(shoppingListItem: ShoppingListItem)
@Query("SELECT * FROM ShoppingListItem")
fun getAllShoppingListItems(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY name ASC")
fun getShoppingListItemsOrderedByNameASC(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY name DESC")
fun getShoppingListItemsOrderedByNameDESC(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY price ASC")
fun getShoppingListItemsOrderedByPriceASC(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY price DESC")
fun getShoppingListItemsOrderedByPriceDESC(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY count ASC")
fun getShoppingListItemsOrderedByCountASC(): Flow<List<ShoppingListItem>>
@Query("SELECT * FROM ShoppingListItem ORDER BY count DESC")
fun getShoppingListItemsOrderedByCountDESC(): Flow<List<ShoppingListItem>>
}
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
import androidx.compose.foundation.layout.Column
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
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Divider
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun ShoppingListScreen(
state: ShoppingListState,
onEvent: (ShoppingListEvent) -> Unit,
onShowToast: (String) -> Unit?
) {
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = {
onEvent(ShoppingListEvent.OpenAddingNewShoppingListItemDialog)
}) {
Icon(imageVector = Icons.Default.Add, contentDescription = "Add new shopping list item")
}
},
modifier = Modifier.padding(16.dp)
) { padding ->
if (state.isAddingNewShoppingListItem || state.isEditingShoppingListItem) {
AddShoppingListItemDialog(state = state, onEvent = onEvent, onShowToast = onShowToast)
}
Column {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Row(Modifier.padding(end = 8.dp)) {
Text(text = "Sort by:", fontSize = 20.sp, fontWeight = FontWeight.Bold)
}
Row(Modifier.horizontalScroll(rememberScrollState())) {
SortType.entries.forEach { sortType ->
Row(
modifier = Modifier
.clickable { onEvent(ShoppingListEvent.SetSortType(sortType)) },
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = state.sortType == sortType,
onClick = { onEvent(ShoppingListEvent.SetSortType(sortType)) })
Text(text = sortType.name, fontSize = 16.sp)
}
}
}
}
Divider(thickness = 2.dp, color = Color.LightGray, modifier = Modifier.padding(bottom = 8.dp))
LazyColumn(
contentPadding = padding,
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(state.shoppingListItems) { shoppingItem ->
Row(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = "${shoppingItem.name} - 🧮 ${shoppingItem.count}",
fontSize = 18.sp
)
Text(text = "💶 ${shoppingItem.price}", fontSize = 16.sp)
}
IconButton(onClick = {
onEvent(ShoppingListEvent.DeleteShoppingListItem(shoppingItem))
.also {
onShowToast("Deleted shopping list item ${shoppingItem.name}")
}
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete Shopping List Item"
)
}
IconButton(onClick = {
onEvent(ShoppingListEvent.OpenEditingShoppingListItem(shoppingItem))
}) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit Shopping List Item"
)
}
}
}
item {
Row(
modifier = Modifier
.width(280.dp)
.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)
}
}
}
}
}
}
@Composable
fun AddShoppingListItemDialog(
state: ShoppingListState,
onEvent: (ShoppingListEvent) -> Unit,
onShowToast: (String) -> Unit?,
modifier: Modifier = Modifier,
) {
AlertDialog(
modifier = modifier,
onDismissRequest = {
if (state.isAddingNewShoppingListItem) {
onEvent(ShoppingListEvent.CloseAddingNewShoppingListItemDialog)
} else if (state.isEditingShoppingListItem) {
onEvent(ShoppingListEvent.CloseEditingShoppingListItem)
}
},
title = { Text(text = "Add new shopping list item") },
text = {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
val shoppingListItem = state.newShoppingListItem
TextField(
value = shoppingListItem.name,
onValueChange = { onEvent(ShoppingListEvent.SetName(it)) },
label = { Text(text = "Item Name") },
placeholder = { Text(text = "Item Name") },
)
TextField(
value = if(shoppingListItem.count>0) shoppingListItem.count.toString() else "",
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))
},
keyboardOptions = KeyboardOptions().copy(
autoCorrect = false,
keyboardType = KeyboardType.Number
),
leadingIcon = {
Text(text = "🧮", fontSize = 24.sp)
}
)
TextField(
value = if(shoppingListItem.price>0) shoppingListItem.price.toString() else "",
label = { Text(text = "Price") },
placeholder = { Text(text = "Price") },
onValueChange = {
var price = it.toFloatOrNull()
if (price===null) {
price = 0f
}
onEvent(ShoppingListEvent.SetPrice(price = price))
},
keyboardOptions = KeyboardOptions().copy(
autoCorrect = false,
keyboardType = KeyboardType.Decimal
),
leadingIcon = {
Text(text = "💶", fontSize = 24.sp)
}
)
}
},
confirmButton = {
Button(onClick = {
if (state.newShoppingListItem.name.length<3){
onShowToast("Item name should not be too short!")
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!") }
}
}) {
Text(text = if (state.isEditingShoppingListItem) "Update" else "Insert")
}
},
dismissButton = {
Button(onClick = {
if (state.isAddingNewShoppingListItem) {
onEvent(ShoppingListEvent.CloseAddingNewShoppingListItemDialog)
} else if (state.isEditingShoppingListItem) {
onEvent(ShoppingListEvent.CloseEditingShoppingListItem)
}
}) {
Text(text = "Close")
}
}
)
}
\ No newline at end of file
package com.example.e13roomshoppinglist
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
enum class SortType{
None,
NameASC,
NameDESC,
PriceASC,
PriceDESC,
CountASC,
CountDESC,
}
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 object InsertNewShoppingListItemToDb: ShoppingListEvent
data class OpenEditingShoppingListItem(val shoppingListItem: ShoppingListItem): ShoppingListEvent
data object CloseEditingShoppingListItem: ShoppingListEvent
data object UpdateCurrentlyEditingShoppingListItem: ShoppingListEvent
data class DeleteShoppingListItem(val shoppingListItem: ShoppingListItem): ShoppingListEvent
data class SetSortType(val sortType: SortType): ShoppingListEvent
}
data class NewShoppingListItem(
val name: String = "",
val price: Float = 0f,
val count: Int = 0
)
data class ShoppingListState(
val shoppingListItems: List<ShoppingListItem> = emptyList(),
val isAddingNewShoppingListItem: Boolean = false,
val newShoppingListItem: NewShoppingListItem = NewShoppingListItem(),
val isEditingShoppingListItem: Boolean = false,
val currentlyEditingShoppingListItem: ShoppingListItem? = null,
val sortType: SortType = SortType.None
)
class ShoppingListViewModel(private val dao: ShoppingListDao): ViewModel() {
private val _state = MutableStateFlow(ShoppingListState())
private val _sortType = MutableStateFlow(SortType.None)
@OptIn(ExperimentalCoroutinesApi::class)
private val _shoppingListItems = _sortType.flatMapLatest { sortType ->
when(sortType) {
SortType.None -> dao.getAllShoppingListItems()
SortType.NameASC -> dao.getShoppingListItemsOrderedByNameASC()
SortType.NameDESC -> dao.getShoppingListItemsOrderedByNameDESC()
SortType.PriceASC -> dao.getShoppingListItemsOrderedByPriceASC()
SortType.PriceDESC -> dao.getShoppingListItemsOrderedByPriceDESC()
SortType.CountASC -> dao.getShoppingListItemsOrderedByCountASC()
SortType.CountDESC -> dao.getShoppingListItemsOrderedByCountDESC()
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
val state = combine(_state, _sortType, _shoppingListItems){
state, sortType, shoppingListItems -> state.copy(shoppingListItems = shoppingListItems, sortType=sortType)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ShoppingListState())
@OptIn(DelicateCoroutinesApi::class)
fun onEvent(event: ShoppingListEvent){
when(event) {
is ShoppingListEvent.SetName -> {
val newShoppingListItem = _state.value.newShoppingListItem
_state.update { it.copy(newShoppingListItem = newShoppingListItem.copy(name = event.name)) }
}
is ShoppingListEvent.SetCount -> {
val newShoppingListItem = _state.value.newShoppingListItem
_state.update { it.copy(newShoppingListItem = newShoppingListItem.copy(count = event.count)) }
}
is ShoppingListEvent.SetPrice -> {
val newShoppingListItem = _state.value.newShoppingListItem
_state.update { it.copy(newShoppingListItem = newShoppingListItem.copy(price = event.price)) }
}
ShoppingListEvent.OpenAddingNewShoppingListItemDialog -> {
_state.update { it.copy(isAddingNewShoppingListItem = true) }
}
ShoppingListEvent.InsertNewShoppingListItemToDb -> {
val newShoppingListItem = _state.value.newShoppingListItem
GlobalScope.launch {
dao.upsertShoppingListItem(ShoppingListItem(
name = newShoppingListItem.name,
price = newShoppingListItem.price,
count = newShoppingListItem.count))
}
_state.update { it.copy(newShoppingListItem=NewShoppingListItem()) }
}
ShoppingListEvent.CloseAddingNewShoppingListItemDialog -> {
_state.update { it.copy(isAddingNewShoppingListItem = false) }
}
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
))
}
}
ShoppingListEvent.UpdateCurrentlyEditingShoppingListItem -> {
val currentlyEditingShoppingListItem = _state.value.currentlyEditingShoppingListItem
if (currentlyEditingShoppingListItem===null) return
val modifiedShoppingListItemAttrs = _state.value.newShoppingListItem
val updatedShoppingListItem = currentlyEditingShoppingListItem.copy(
name = modifiedShoppingListItemAttrs.name,
price = modifiedShoppingListItemAttrs.price,
count = modifiedShoppingListItemAttrs.count
)
GlobalScope.launch { dao.upsertShoppingListItem(updatedShoppingListItem) }
_state.update {
it.copy(
currentlyEditingShoppingListItem = null,
newShoppingListItem = NewShoppingListItem(),
isEditingShoppingListItem = false)
}
}
ShoppingListEvent.CloseEditingShoppingListItem -> {
_state.update {
it.copy(
isEditingShoppingListItem = false,
currentlyEditingShoppingListItem = null,
newShoppingListItem = NewShoppingListItem())
}
}
is ShoppingListEvent.DeleteShoppingListItem -> {
GlobalScope.launch {
dao.deleteShoppingListItem(event.shoppingListItem)
}
}
is ShoppingListEvent.SetSortType -> {
_sortType.update { event.sortType }
}
}
}
}
package com.example.e13roomshoppinglist.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
package com.example.e13roomshoppinglist.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun E13RoomShoppingListTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment