From 94b6b2039c9fe4f003db59db5d02a29f3f0d495a Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:31:01 +0000 Subject: [PATCH 1/7] Start draft PR From 1d4861bde067e7977eaae86df3f91ebfe11eb0bb Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:31:20 +0000 Subject: [PATCH 2/7] Add TodoItem data class with Room annotations --- .../com/example/todoapp/data/model/TodoItem.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 app/src/main/java/com/example/todoapp/data/model/TodoItem.kt diff --git a/app/src/main/java/com/example/todoapp/data/model/TodoItem.kt b/app/src/main/java/com/example/todoapp/data/model/TodoItem.kt new file mode 100644 index 0000000..444176d --- /dev/null +++ b/app/src/main/java/com/example/todoapp/data/model/TodoItem.kt @@ -0,0 +1,15 @@ +package com.example.todoapp.data.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "todo_items") +data class TodoItem( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + val title: String, + val description: String? = null, + val isCompleted: Boolean = false, + val createdAt: Long = System.currentTimeMillis(), + val updatedAt: Long = System.currentTimeMillis() +) \ No newline at end of file From 776f1108373a966688e185ccb1366996bec18ab4 Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:31:32 +0000 Subject: [PATCH 3/7] Implement TodoItemDao with comprehensive CRUD methods --- .../example/todoapp/data/dao/TodoItemDao.kt | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 app/src/main/java/com/example/todoapp/data/dao/TodoItemDao.kt diff --git a/app/src/main/java/com/example/todoapp/data/dao/TodoItemDao.kt b/app/src/main/java/com/example/todoapp/data/dao/TodoItemDao.kt new file mode 100644 index 0000000..166051b --- /dev/null +++ b/app/src/main/java/com/example/todoapp/data/dao/TodoItemDao.kt @@ -0,0 +1,80 @@ +package com.example.todoapp.data.dao + +import androidx.room.* +import com.example.todoapp.data.model.TodoItem +import kotlinx.coroutines.flow.Flow + +@Dao +interface TodoItemDao { + /** + * Inserts a new todo item into the database. + * @param todoItem The todo item to insert + * @return The ID of the newly inserted item + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(todoItem: TodoItem): Long + + /** + * Inserts multiple todo items into the database. + * @param todoItems The list of todo items to insert + * @return List of inserted item IDs + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(todoItems: List): List + + /** + * Updates an existing todo item. + * @param todoItem The todo item with updated information + */ + @Update + suspend fun update(todoItem: TodoItem) + + /** + * Deletes a specific todo item. + * @param todoItem The todo item to delete + */ + @Delete + suspend fun delete(todoItem: TodoItem) + + /** + * Retrieves a todo item by its ID. + * @param id The ID of the todo item + * @return The todo item or null if not found + */ + @Query("SELECT * FROM todo_items WHERE id = :id") + suspend fun getById(id: Long): TodoItem? + + /** + * Retrieves all todo items, sorted by creation date. + * @return A Flow of todo items + */ + @Query("SELECT * FROM todo_items ORDER BY createdAt DESC") + fun getAllTodoItems(): Flow> + + /** + * Retrieves completed todo items. + * @return A Flow of completed todo items + */ + @Query("SELECT * FROM todo_items WHERE isCompleted = 1 ORDER BY updatedAt DESC") + fun getCompletedTodoItems(): Flow> + + /** + * Retrieves active (not completed) todo items. + * @return A Flow of active todo items + */ + @Query("SELECT * FROM todo_items WHERE isCompleted = 0 ORDER BY createdAt DESC") + fun getActiveTodoItems(): Flow> + + /** + * Deletes all todo items from the database. + */ + @Query("DELETE FROM todo_items") + suspend fun deleteAll() + + /** + * Counts the total number of todo items. + * @return The total number of todo items + */ + @Query("SELECT COUNT(*) FROM todo_items") + suspend fun count(): Int +} \ No newline at end of file From ba166e313cc5a88981f771349d1b74472b2b848e Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:31:46 +0000 Subject: [PATCH 4/7] Add comprehensive tests for TodoItemDao --- .../todoapp/data/dao/TodoItemDaoTest.kt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 app/src/test/java/com/example/todoapp/data/dao/TodoItemDaoTest.kt diff --git a/app/src/test/java/com/example/todoapp/data/dao/TodoItemDaoTest.kt b/app/src/test/java/com/example/todoapp/data/dao/TodoItemDaoTest.kt new file mode 100644 index 0000000..63e7a48 --- /dev/null +++ b/app/src/test/java/com/example/todoapp/data/dao/TodoItemDaoTest.kt @@ -0,0 +1,104 @@ +package com.example.todoapp.data.dao + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.todoapp.data.model.TodoItem +import com.example.todoapp.data.database.TodoDatabase +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +@RunWith(AndroidJUnit4::class) +class TodoItemDaoTest { + private lateinit var todoItemDao: TodoItemDao + private lateinit var database: TodoDatabase + + @Before + fun createDatabase() { + val context = ApplicationProvider.getApplicationContext() + database = Room.inMemoryDatabaseBuilder(context, TodoDatabase::class.java) + .allowMainThreadQueries() + .build() + todoItemDao = database.todoItemDao() + } + + @After + @Throws(IOException::class) + fun closeDatabase() { + database.close() + } + + @Test + @Throws(Exception::class) + fun insertAndGetTodoItem() = runBlocking { + val todoItem = TodoItem(title = "Test Todo", description = "Test Description") + val id = todoItemDao.insert(todoItem) + val retrievedItem = todoItemDao.getById(id) + + assertNotNull(retrievedItem) + assertEquals("Test Todo", retrievedItem?.title) + assertEquals("Test Description", retrievedItem?.description) + } + + @Test + @Throws(Exception::class) + fun updateTodoItem() = runBlocking { + val todoItem = TodoItem(title = "Original Title") + val id = todoItemDao.insert(todoItem) + + val updatedItem = todoItem.copy(id = id, title = "Updated Title") + todoItemDao.update(updatedItem) + + val retrievedItem = todoItemDao.getById(id) + assertEquals("Updated Title", retrievedItem?.title) + } + + @Test + @Throws(Exception::class) + fun deleteTodoItem() = runBlocking { + val todoItem = TodoItem(title = "Item to Delete") + val id = todoItemDao.insert(todoItem) + + val retrievedItem = todoItemDao.getById(id) + assertNotNull(retrievedItem) + + todoItemDao.delete(retrievedItem!!) + val deletedItem = todoItemDao.getById(id) + assertNull(deletedItem) + } + + @Test + @Throws(Exception::class) + fun getAllTodoItems() = runBlocking { + val todoItems = listOf( + TodoItem(title = "Item 1"), + TodoItem(title = "Item 2"), + TodoItem(title = "Item 3") + ) + todoItemDao.insertAll(todoItems) + + val allItems = todoItemDao.getAllTodoItems().first() + assertEquals(3, allItems.size) + } + + @Test + @Throws(Exception::class) + fun getCompletedTodoItems() = runBlocking { + val todoItems = listOf( + TodoItem(title = "Completed 1", isCompleted = true), + TodoItem(title = "Completed 2", isCompleted = true), + TodoItem(title = "Active", isCompleted = false) + ) + todoItemDao.insertAll(todoItems) + + val completedItems = todoItemDao.getCompletedTodoItems().first() + assertEquals(2, completedItems.size) + } +} \ No newline at end of file From c5fae17afa22798ad23aa71b5cb762f3c9d7ff72 Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:31:55 +0000 Subject: [PATCH 5/7] Add comprehensive .gitignore for Android project --- .gitignore | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f44d5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Android Studio +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild + +# Gradle files +gradle-app.setting +!gradle-wrapper.jar +.gradletasknamecache + +# Compiled class files +*.class + +# Log files +*.log + +# Package Files +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# Virtual machine crash logs +hs_err_pid* + +# Android specific +bin/ +gen/ +out/ +release/ + +# Android Profiling +*.hprof + +# Dependency directories +/node_modules +/jspm_packages + +# Editor directories and files +.idea/ +.vscode/ +*.swp +*.swo + +# Environment files +.env + +# Secrets +*.key +secrets.json + +# Build output +/build +/dist +/target \ No newline at end of file From 06512742ec2ad86dcc00e21ef4f1241f51e9ec9a Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:32:02 +0000 Subject: [PATCH 6/7] Add TodoDatabase with singleton instance creation --- .../todoapp/data/database/TodoDatabase.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/src/main/java/com/example/todoapp/data/database/TodoDatabase.kt diff --git a/app/src/main/java/com/example/todoapp/data/database/TodoDatabase.kt b/app/src/main/java/com/example/todoapp/data/database/TodoDatabase.kt new file mode 100644 index 0000000..5b21a49 --- /dev/null +++ b/app/src/main/java/com/example/todoapp/data/database/TodoDatabase.kt @@ -0,0 +1,30 @@ +package com.example.todoapp.data.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.example.todoapp.data.dao.TodoItemDao +import com.example.todoapp.data.model.TodoItem + +@Database(entities = [TodoItem::class], version = 1, exportSchema = false) +abstract class TodoDatabase : RoomDatabase() { + abstract fun todoItemDao(): TodoItemDao + + companion object { + @Volatile + private var INSTANCE: TodoDatabase? = null + + fun getDatabase(context: Context): TodoDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + TodoDatabase::class.java, + "todo_database" + ).build() + INSTANCE = instance + instance + } + } + } +} \ No newline at end of file From d8ac8d6687a6bfbce0044f9067b591f91f18a03d Mon Sep 17 00:00:00 2001 From: valin1993 Date: Sat, 28 Jun 2025 17:32:16 +0000 Subject: [PATCH 7/7] Add build.gradle.kts with Room and testing dependencies --- app/build.gradle.kts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/build.gradle.kts diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..fa48271 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-kapt") +} + +android { + compileSdk = 33 + defaultConfig { + applicationId = "com.example.todoapp" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} + +dependencies { + // Room dependencies + val roomVersion = "2.5.1" + implementation("androidx.room:room-runtime:$roomVersion") + implementation("androidx.room:room-ktx:$roomVersion") + kapt("androidx.room:room-compiler:$roomVersion") + + // Testing dependencies + testImplementation("androidx.room:room-testing:$roomVersion") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} \ No newline at end of file