Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ plugins {
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp") version "2.1.0-1.0.29"
id("org.jetbrains.kotlin.plugin.compose")
//Room

}

android {
Expand Down Expand Up @@ -79,4 +81,9 @@ dependencies {
// Testing
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.ext:junit:1.2.1")

//Room
implementation("androidx.room:room-runtime:2.8.3")
ksp("androidx.room:room-compiler:${rootProject.extra["room_version"]}")
// implementation("android.room:room-ktx:${rootProject.extra["room_version"]}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ class InventoryApplication : Application() {

override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
container = AppDataContainer(context = this)
}
}
}
4 changes: 2 additions & 2 deletions app/src/main/java/com/example/inventory/data/AppContainer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ class AppDataContainer(private val context: Context) : AppContainer {
* Implementation for [ItemsRepository]
*/
override val itemsRepository: ItemsRepository by lazy {
OfflineItemsRepository()
OfflineItemsRepository(InventoryDatabase.getDatabase(context).itemDao())
}
}
}
28 changes: 28 additions & 0 deletions app/src/main/java/com/example/inventory/data/InventoryDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.inventory.data

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class InventoryDatabase: RoomDatabase() {
abstract fun itemDao(): ItemDao

companion object {
@Volatile
private var Instance: InventoryDatabase? = null
fun getDatabase(context: Context): InventoryDatabase {
return Instance ?: synchronized(this) {
Room.databaseBuilder(
context = context,
InventoryDatabase::class.java,
"item_database"
)
.fallbackToDestructiveMigration(false)
.build()
.also { Instance = it }
}
}
}
}
7 changes: 6 additions & 1 deletion app/src/main/java/com/example/inventory/data/Item.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@

package com.example.inventory.data

import androidx.room.Entity
import androidx.room.PrimaryKey


/**
* Entity data class represents a single row in the database.
*/
class Item(
@Entity(tableName = "items")
data class Item(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val name: String,
val price: Double,
Expand Down
27 changes: 27 additions & 0 deletions app/src/main/java/com/example/inventory/data/ItemDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.inventory.data

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow

@Dao
interface ItemDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: Item)

@Update
suspend fun update(item: Item)

@Delete
suspend fun delete(item: Item)

@Query("SELECT * FROM items WHERE id = :id")
fun getItem(id: Int): Flow<Item>

@Query("SELECT * FROM items ORDER BY name ASC")
fun getAllItems(): Flow<List<Item>>
}
29 changes: 28 additions & 1 deletion app/src/main/java/com/example/inventory/data/ItemsRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,34 @@

package com.example.inventory.data

import kotlinx.coroutines.flow.Flow

/**
* Repository that provides insert, update, delete, and retrieve of [Item] from a given data source.
*/
interface ItemsRepository
interface ItemsRepository {
/**
* Retrieve all the items from the given data source
*/
fun getAllItemsStream(): Flow<List<Item>>

/**
* Retrieve an item from the given data source that matches the given [id]
*/
fun getItemStream(id: Int): Flow<Item?>

/**
* Insert [item] in the data source
*/
suspend fun insertItem(item: Item)

/**
* Delete [item] from the data source
*/
suspend fun deleteItem(item: Item)

/**
* Update [item] in the data source
*/
suspend fun updateItem(item: Item)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,17 @@

package com.example.inventory.data

class OfflineItemsRepository : ItemsRepository
import kotlinx.coroutines.flow.Flow

class OfflineItemsRepository(private val itemDao: ItemDao) : ItemsRepository {
override fun getAllItemsStream(): Flow<List<Item>> = itemDao.getAllItems()

override fun getItemStream(id: Int): Flow<Item?> = itemDao.getItem(id)

override suspend fun insertItem(item: Item) = itemDao.insert(item = item)

override suspend fun deleteItem(item: Item) = itemDao.delete(item = item)

override suspend fun updateItem(item: Item) = itemDao.update(item = item)
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object AppViewModelProvider {
}
// Initializer for ItemEntryViewModel
initializer {
ItemEntryViewModel()
ItemEntryViewModel(inventoryApplication().container.itemsRepository)
}

// Initializer for ItemDetailsViewModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ private fun InventoryList(
InventoryItem(item = item,
modifier = Modifier
.padding(dimensionResource(id = R.dimen.padding_small))
.clickable { onItemClick(item) })
.clickable { onItemClick(item) }
)
}
}
}
Expand Down
22 changes: 15 additions & 7 deletions app/src/main/java/com/example/inventory/ui/item/ItemEntryScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
Expand All @@ -45,6 +46,7 @@ import com.example.inventory.R
import com.example.inventory.ui.AppViewModelProvider
import com.example.inventory.ui.navigation.NavigationDestination
import com.example.inventory.ui.theme.InventoryTheme
import kotlinx.coroutines.launch
import java.util.Currency
import java.util.Locale

Expand All @@ -59,29 +61,35 @@ fun ItemEntryScreen(
navigateBack: () -> Unit,
onNavigateUp: () -> Unit,
canNavigateBack: Boolean = true,
viewModel: ItemEntryViewModel = viewModel(factory = AppViewModelProvider.Factory)
viewModel: ItemEntryViewModel = viewModel(factory = AppViewModelProvider.Factory),
) {
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
InventoryTopAppBar(
title = stringResource(ItemEntryDestination.titleRes),
canNavigateBack = canNavigateBack,
navigateUp = onNavigateUp
navigateUp = onNavigateUp,
)
}
) { innerPadding ->
ItemEntryBody(
itemUiState = viewModel.itemUiState,
onItemValueChange = viewModel::updateUiState,
onSaveClick = { },
onSaveClick = {
coroutineScope.launch {
viewModel.saveItem()
navigateBack()
}
},
modifier = Modifier
.padding(
start = innerPadding.calculateStartPadding(LocalLayoutDirection.current),
end = innerPadding.calculateEndPadding(LocalLayoutDirection.current),
top = innerPadding.calculateTopPadding()
)
.verticalScroll(rememberScrollState())
.fillMaxWidth()
.fillMaxWidth(),
)
}
}
Expand All @@ -91,11 +99,11 @@ fun ItemEntryBody(
itemUiState: ItemUiState,
onItemValueChange: (ItemDetails) -> Unit,
onSaveClick: () -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
) {
Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.padding_large)),
modifier = modifier.padding(dimensionResource(id = R.dimen.padding_medium))
modifier = modifier.padding(dimensionResource(id = R.dimen.padding_medium)),
) {
ItemInputForm(
itemDetails = itemUiState.itemDetails,
Expand Down Expand Up @@ -175,7 +183,7 @@ fun ItemInputForm(
}
}

@Preview(showBackground = true)
@Preview(showBackground = true) // Preview
@Composable
private fun ItemEntryScreenPreview() {
InventoryTheme {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.inventory.data.Item
import com.example.inventory.data.ItemsRepository
import java.text.NumberFormat

/**
* ViewModel to validate and insert items in the Room database.
*/
class ItemEntryViewModel : ViewModel() {
class ItemEntryViewModel(private val itemsRepository: ItemsRepository) : ViewModel() {

/**
* Holds current item ui state
Expand All @@ -48,6 +49,12 @@ class ItemEntryViewModel : ViewModel() {
name.isNotBlank() && price.isNotBlank() && quantity.isNotBlank()
}
}

suspend fun saveItem() {
if (validateInput()) {
itemsRepository.insertItem(itemUiState.itemDetails.toItem())
}
}
}

/**
Expand Down Expand Up @@ -97,4 +104,4 @@ fun Item.toItemDetails(): ItemDetails = ItemDetails(
name = name,
price = price.toString(),
quantity = quantity.toString()
)
)
7 changes: 3 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ buildscript {
set("room_version", "2.6.0")
}
}

plugins {
id("com.android.application") version "8.8.0" apply false
id("com.android.library") version "8.8.0" apply false
id("com.android.application") version "8.13.1" apply false
id("com.android.library") version "8.13.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.1.0" apply false
}

tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
delete(rootProject.layout.buildDirectory)
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down