Skip to content
Draft
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
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
}

android {
Expand Down Expand Up @@ -51,6 +52,9 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.10.0")
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
13 changes: 9 additions & 4 deletions app/src/main/java/com/example/composebasics/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -23,17 +21,24 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.composebasics.data.local.TaskDatabase
import com.example.composebasics.data.repository.RoomTaskRepositoryImpl
import com.example.composebasics.ui.home.HomeScreen
import com.example.composebasics.ui.home.HomeViewModelFactory
import com.example.composebasics.ui.theme.ComposeBasicsTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()

val database = TaskDatabase.getDatabase(this)
val repository = RoomTaskRepositoryImpl(database.taskDao())
val factory = HomeViewModelFactory(repository)

setContent {
ComposeBasicsTheme {

HomeScreen()
HomeScreen(viewModelFactory = factory)
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/example/composebasics/data/local/TaskDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.composebasics.data.local

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

@Dao
interface TaskDao {
@Query("SELECT * FROM tasks ORDER BY rowid ASC")
fun getAllTasks(): Flow<List<TaskEntity>>

@Query("SELECT * FROM tasks WHERE id = :taskId")
suspend fun getTaskById(taskId: String): TaskEntity?

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTask(task: TaskEntity)

@Update
suspend fun updateTask(task: TaskEntity)

@Query("DELETE FROM tasks WHERE id = :taskId")
suspend fun deleteTask(taskId: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.composebasics.data.local

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

@Database(entities = [TaskEntity::class], version = 1, exportSchema = false)
abstract class TaskDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao

companion object {
@Volatile
private var INSTANCE: TaskDatabase? = null

fun getDatabase(context: Context): TaskDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
TaskDatabase::class.java,
"task_database"
).build()
INSTANCE = instance
instance
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.composebasics.data.local

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

@Entity(tableName = "tasks")
data class TaskEntity(
@PrimaryKey val id: String,
val title: String,
val isCompleted: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.composebasics.data.repository

import com.example.composebasics.data.local.TaskDao
import com.example.composebasics.data.local.TaskEntity
import com.example.composebasics.domain.model.Task
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import java.util.UUID

class RoomTaskRepositoryImpl(private val dao: TaskDao) : TaskRepository {

override fun getTasks(): Flow<List<Task>> {
return dao.getAllTasks().map { entities ->
entities.map { it.toTask() }
}
}

override suspend fun addTask(title: String) {
val entity = TaskEntity(
id = UUID.randomUUID().toString(),
title = title,
isCompleted = false
)
dao.insertTask(entity)
}

override suspend fun toggleTask(taskId: String) {
val entity = dao.getTaskById(taskId) ?: return
dao.updateTask(entity.copy(isCompleted = !entity.isCompleted))
}

override suspend fun deleteTask(taskId: String) {
dao.deleteTask(taskId)
}
}

fun TaskEntity.toTask() = Task(id = id, title = title, isCompleted = isCompleted)
fun Task.toEntity() = TaskEntity(id = id, title = title, isCompleted = isCompleted)
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.example.composebasics.data.repository

import com.example.composebasics.domain.model.Task
import kotlinx.coroutines.flow.Flow

interface TaskRepository {
fun getTasks(): List<Task>
fun addTask(title: String): List<Task>
fun toggleTask(taskId: String): List<Task>
fun deleteTask(taskId: String): List<Task>
}
fun getTasks(): Flow<List<Task>>
suspend fun addTask(title: String)
suspend fun toggleTask(taskId: String)
suspend fun deleteTask(taskId: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package com.example.composebasics.data.repository
import com.example.composebasics.domain.model.Task
import java.util.UUID

class TaskRepositoryImpl: TaskRepository{
class TaskRepositoryImpl {

private val tasks = mutableListOf<Task>()
override fun getTasks(): List<Task> {
fun getTasks(): List<Task> {
return tasks.toList()
}

override fun addTask(title: String): List<Task> {
fun addTask(title: String): List<Task> {
val newTask = Task(
id = UUID.randomUUID().toString(),
title = title,
Expand All @@ -20,7 +20,7 @@ class TaskRepositoryImpl: TaskRepository{
return tasks.toList()
}

override fun toggleTask(taskId: String): List<Task> {
fun toggleTask(taskId: String): List<Task> {
val updatedTasks = tasks.map { task ->
if (task.id == taskId) {
task.copy(isCompleted = !task.isCompleted)
Expand All @@ -35,7 +35,7 @@ class TaskRepositoryImpl: TaskRepository{
return tasks.toList()
}

override fun deleteTask(taskId: String): List<Task> {
fun deleteTask(taskId: String): List<Task> {
tasks.removeAll { it.id == taskId }
return tasks.toList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
@Preview
fun HomeScreen(
viewModel: HomeViewModel = viewModel()
viewModelFactory: ViewModelProvider.Factory? = null,
viewModel: HomeViewModel = viewModel(factory = viewModelFactory)
) {
val state by viewModel.uiState.collectAsState()
var title by remember { mutableStateOf("") }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
package com.example.composebasics.ui.home

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.composebasics.data.repository.TaskRepository
import com.example.composebasics.data.repository.TaskRepositoryImpl
import com.example.composebasics.domain.model.Task
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class HomeViewModel: ViewModel() {
class HomeViewModel(private val repository: TaskRepository) : ViewModel() {

private val repository: TaskRepository = TaskRepositoryImpl()
private val _uiState = MutableStateFlow(HomeUIState(task = repository.getTasks()))
private val _uiState = MutableStateFlow(HomeUIState())
val uiState: StateFlow<HomeUIState> = _uiState

init {
viewModelScope.launch {
repository.getTasks().collect { tasks ->
_uiState.update { it.copy(task = tasks) }
}
}
}

fun addTask(title: String) {
val updatedTasks = repository.addTask(title)
_uiState.value = _uiState.value.copy(task = updatedTasks)
viewModelScope.launch { repository.addTask(title) }
}

fun toggleTask(taskId: String) {
val updatedTasks = repository.toggleTask(taskId)
_uiState.value = _uiState.value.copy(task = updatedTasks)
viewModelScope.launch { repository.toggleTask(taskId) }
}

fun deleteTask(taskId: String) {
val updatedTasks = repository.deleteTask(taskId)
_uiState.value = _uiState.value.copy(task = updatedTasks)
viewModelScope.launch { repository.deleteTask(taskId) }
}

fun setFilter(filter: TaskFilter) {
_uiState.value = _uiState.value.copy(filter = filter)
_uiState.update { it.copy(filter = filter) }
}

fun getVisibleTasks(state: HomeUIState): List<Task> {
return when (state.filter) {
TaskFilter.ALL -> state.task
TaskFilter.ACTIVE -> state.task.filter { !it.isCompleted }
TaskFilter.COMPLETED -> state.task.filter { it.isCompleted }

}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.composebasics.ui.home

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.composebasics.data.repository.TaskRepository

class HomeViewModelFactory(private val repository: TaskRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(HomeViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return HomeViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[versions]
agp = "8.13.2"
kotlin = "2.0.21"
ksp = "2.0.21-1.0.28"
room = "2.7.1"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
Expand All @@ -24,9 +26,13 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }