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
92 changes: 92 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Android Studio
*.iml
<<<<<<< HEAD
.gradle
/local.properties
/.idea/
.DS_Store
/build
/captures
.externalNativeBuild
.cxx

# Gradle files
gradle/
gradlew
gradlew.bat

# Compiled class files
*.class

# Log files
*.log

# IntelliJ
out/

# Kotlin build files
build/
*.hprof
=======
.gradle/
build/
local.properties
.idea/

# Kotlin and Android specific
*.class
*.log
*.ctxt
.mtj.tmp/
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
>>>>>>> pr-4-gapcomputer-kotlinTodoApp

# Virtual machine crash logs
hs_err_pid*

<<<<<<< HEAD
# Dependency directories
node_modules/
/app/build/

# Local configuration file
local.properties

# Android Profiling
*.hprof

# Sensitive or local files
.env

# Temp files
*.swp
*~
.tmp

# Logs
logs/
=======
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Dependency directories
/node_modules
/captures
/app/build

# Environment and local configuration
.env
*.keystore
>>>>>>> pr-4-gapcomputer-kotlinTodoApp
49 changes: 49 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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"
}

buildFeatures {
viewBinding = true
}
}

dependencies {
// Kotlin standard library
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.0")

// AndroidX
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.0")

// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("org.robolectric:robolectric:4.9")
testImplementation("org.mockito:mockito-core:4.8.0")
testImplementation("androidx.test:core:1.5.0")

// Android Testing
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

tasks.withType<Test> {
configure<JacocoTaskExtension> {
isEnabled = true
}
}
45 changes: 45 additions & 0 deletions app/src/main/java/com/example/todoapp/data/dao/TodoDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.example.todoapp.data.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.todoapp.data.entity.TodoItem

@Dao
interface TodoDao {
/**
* Inserts a new todo item into the database
*
* @param todoItem The todo item to be inserted
* @return The ID of the inserted item
*/
@Insert
suspend fun insert(todoItem: TodoItem): Long

/**
* Retrieves all todo items from the database
*
* @return A list of all todo items
*/
@Query("SELECT * FROM todo_items")
suspend fun getAllTodos(): List<TodoItem>

/**
* Deletes a specific todo item from the database by its ID
*
* @param id The unique identifier of the todo item to be deleted
* @return Number of rows affected (should be 1 if deletion is successful)
*/
@Query("DELETE FROM todo_items WHERE id = :id")
suspend fun deleteTodoById(id: Long): Int

/**
* Alternative deletion method using the entire TodoItem object
*
* @param todoItem The todo item to be deleted
*/
@Delete
suspend fun deleteTodo(todoItem: TodoItem)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.todoapp.data.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.example.todoapp.data.dao.TodoDao
import com.example.todoapp.data.entity.TodoItem

@Database(entities = [TodoItem::class], version = 1, exportSchema = false)
abstract class TodoDatabase : RoomDatabase() {
abstract fun todoDao(): TodoDao

companion object {
// Singleton implementation can be added here in future
}
}
20 changes: 20 additions & 0 deletions app/src/main/java/com/example/todoapp/data/entity/TodoItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.todoapp.data.entity

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

@Entity(tableName = "todo_items")
data class TodoItem(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,

@ColumnInfo(name = "title")
val title: String,

@ColumnInfo(name = "description")
val description: String,

@ColumnInfo(name = "is_completed")
val isCompleted: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.example.todoapp.ui.adapter

import android.app.AlertDialog
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.R
import com.example.todoapp.data.entity.TodoItem
import com.example.todoapp.databinding.ItemTodoBinding
import com.example.todoapp.ui.viewmodel.TodoViewModel

/**
* RecyclerView adapter for displaying and managing Todo items
*
* @property context Android context for showing dialogs
* @property viewModel ViewModel for handling Todo item operations
* @property todoList List of Todo items to display
*/
class TodoListAdapter(
private val context: Context,
private val viewModel: TodoViewModel,
private var todoList: List<TodoItem>
) : RecyclerView.Adapter<TodoListAdapter.TodoViewHolder>() {

/**
* Shows a confirmation dialog before deleting a Todo item
*
* @param todo The Todo item to be deleted
*/
private fun showDeleteConfirmationDialog(todo: TodoItem) {
AlertDialog.Builder(context)
.setTitle(R.string.delete_todo_title)
.setMessage(R.string.delete_todo_message)
.setPositiveButton(R.string.delete) { dialog, _ ->
// Confirm deletion
viewModel.deleteTodo(todo)
dialog.dismiss()
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
// Cancel deletion
dialog.dismiss()
}
.create()
.show()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
val binding = ItemTodoBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return TodoViewHolder(binding)
}

override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val todo = todoList[position]
holder.bind(todo)
}

override fun getItemCount(): Int = todoList.size

inner class TodoViewHolder(private val binding: ItemTodoBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(todo: TodoItem) {
binding.apply {
// Set todo details
todoTitle.text = todo.title
todoDescription.text = todo.description

// Delete button click listener with confirmation dialog
deleteButton.setOnClickListener {
showDeleteConfirmationDialog(todo)
}
}
}
}

/**
* Update the list of Todo items and notify the adapter
*
* @param newList New list of Todo items
*/
fun updateList(newList: List<TodoItem>) {
todoList = newList
notifyDataSetChanged()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.todoapp.ui.viewmodel

import androidx.lifecycle.ViewModel
import com.example.todoapp.data.entity.TodoItem
import com.example.todoapp.data.dao.TodoDao
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class TodoViewModel(private val todoDao: TodoDao) : ViewModel() {
fun deleteTodo(todoItem: TodoItem) {
CoroutineScope(Dispatchers.IO).launch {
todoDao.deleteTodo(todoItem)
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- General app strings -->
<string name="app_name">Todo App</string>

<!-- Delete Confirmation Dialog Strings -->
<string name="delete_todo_title">Delete Todo</string>
<string name="delete_todo_message">Are you sure you want to delete this todo item?</string>
<string name="delete">Delete</string>
<string name="cancel">Cancel</string>
</resources>
Loading