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

<<<<<<< HEAD
# Kotlin/Android specific
bin/
gen/
out/

# Dependency directories
/node_modules
/app/build/

# Logs
*.log

# Environment files
.env

# Gradle
.gradle/
build/

# VS Code
.vscode/

# MacOS
.DS_Store

# Temp files
*.swp
*~
=======
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# Virtual machine crash logs
hs_err_pid*

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
/captures

# IntelliJ
*.iml
.idea/

# Keystore files
*.jks
*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Gradle files
.gradle/
build/
>>>>>>> pr-2-Aflame7121-kotlinTodoApp
86 changes: 86 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
plugins {
id("com.android.application")
id("kotlin-android")
<<<<<<< HEAD
=======
id("kotlin-kapt")
>>>>>>> pr-2-Aflame7121-kotlinTodoApp
}

android {
compileSdk = 33
defaultConfig {
applicationId = "com.todoapp"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

<<<<<<< HEAD
=======
buildFeatures {
viewBinding = true
}

>>>>>>> pr-2-Aflame7121-kotlinTodoApp
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
<<<<<<< HEAD

=======
>>>>>>> pr-2-Aflame7121-kotlinTodoApp
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
<<<<<<< HEAD

=======
>>>>>>> pr-2-Aflame7121-kotlinTodoApp
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
<<<<<<< HEAD
// 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.10.3")
testImplementation("androidx.test:core:1.5.0")
testImplementation("androidx.test.ext:junit:1.1.5")
testImplementation("org.mockito:mockito-core:4.8.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
=======
// Android Core
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")

// ViewModel and LiveData
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")

// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("androidx.test:core:1.5.0")
testImplementation("androidx.test.ext:junit:1.1.5")
testImplementation("org.robolectric:robolectric:4.9")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
>>>>>>> pr-2-Aflame7121-kotlinTodoApp
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/todoapp/data/Todo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.todoapp.data

data class Todo(
val id: Long = 0,
val title: String,
val description: String? = null,
val isCompleted: Boolean = false,
val createdAt: Long = System.currentTimeMillis()
)
10 changes: 10 additions & 0 deletions app/src/main/java/com/todoapp/model/TodoItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.todoapp.model

import java.util.Date

data class TodoItem(
val id: Long,
val title: String,
val dueDate: Date? = null,
val isCompleted: Boolean = false
)
67 changes: 67 additions & 0 deletions app/src/main/java/com/todoapp/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.todoapp.ui

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.todoapp.R
import com.todoapp.databinding.ActivityMainBinding
import com.todoapp.model.TodoItem
import java.util.Date

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var todoViewModel: TodoViewModel
private lateinit var todoListAdapter: TodoListAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Inflate the layout using view binding
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// Initialize ViewModel
todoViewModel = ViewModelProvider(this).get(TodoViewModel::class.java)

// Setup RecyclerView
setupRecyclerView()

// Observe todo items and update adapter
todoViewModel.todoItems.observe(this) { todos ->
todoListAdapter.submitList(todos)
}

// Setup Add Todo button
binding.addTodoButton.setOnClickListener {
val todoText = binding.todoEditText.text.toString().trim()
if (todoText.isNotEmpty()) {
val newTodo = TodoItem(
id = System.currentTimeMillis(), // Unique ID
title = todoText,
dueDate = Date(), // Current date
isCompleted = false
)
todoViewModel.addTodo(newTodo)
binding.todoEditText.text.clear()
}
}
}

private fun setupRecyclerView() {
todoListAdapter = TodoListAdapter(
onItemClick = { todo ->
// Edit todo - for now, just print or log
println("Clicked todo: $todo")
},
onCompletionToggle = { todo ->
todoViewModel.toggleTodoCompletion(todo)
}
)

binding.todoRecyclerView.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = todoListAdapter
}
}
}
52 changes: 52 additions & 0 deletions app/src/main/java/com/todoapp/ui/TodoAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.todoapp.ui

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.todoapp.data.Todo
import com.todoapp.databinding.ItemTodoBinding

class TodoAdapter(
private val onItemClick: (Todo) -> Unit,
private val onDeleteClick: (Todo) -> Unit,
private val onCompleteToggle: (Todo) -> Unit
) : ListAdapter<Todo, TodoAdapter.TodoViewHolder>(TodoDiffCallback()) {

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

override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
holder.bind(getItem(position))
}

class TodoViewHolder(
private val binding: ItemTodoBinding,
private val onItemClick: (Todo) -> Unit,
private val onDeleteClick: (Todo) -> Unit,
private val onCompleteToggle: (Todo) -> Unit
) : RecyclerView.ViewHolder(binding.root) {
fun bind(todo: Todo) {
binding.todoTitleTextView.text = todo.title
binding.todoCheckBox.isChecked = todo.isCompleted

// Set click listeners
binding.root.setOnClickListener { onItemClick(todo) }
binding.deleteButton.setOnClickListener { onDeleteClick(todo) }
binding.todoCheckBox.setOnCheckedChangeListener { _, _ -> onCompleteToggle(todo) }
}
}

class TodoDiffCallback : DiffUtil.ItemCallback<Todo>() {
override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean {
return oldItem == newItem
}
}
}
47 changes: 47 additions & 0 deletions app/src/main/java/com/todoapp/ui/TodoViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.todoapp.ui

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.todoapp.model.TodoItem

class TodoViewModel : ViewModel() {
// Mutable list of todo items
private val _todoItems = MutableLiveData<List<TodoItem>>(emptyList())
val todoItems: LiveData<List<TodoItem>> = _todoItems

// Add a new todo item
fun addTodo(todoItem: TodoItem) {
val currentList = _todoItems.value?.toMutableList() ?: mutableListOf()
currentList.add(todoItem)
_todoItems.value = currentList
}

// Update an existing todo item
fun updateTodo(updatedTodoItem: TodoItem) {
val currentList = _todoItems.value?.toMutableList() ?: return
val index = currentList.indexOfFirst { it.id == updatedTodoItem.id }
if (index != -1) {
currentList[index] = updatedTodoItem
_todoItems.value = currentList
}
}

// Delete a todo item
fun deleteTodo(todoItem: TodoItem) {
val currentList = _todoItems.value?.toMutableList() ?: return
currentList.removeAll { it.id == todoItem.id }
_todoItems.value = currentList
}

// Toggle todo completion status
fun toggleTodoCompletion(todoItem: TodoItem) {
val currentList = _todoItems.value?.toMutableList() ?: return
val index = currentList.indexOfFirst { it.id == todoItem.id }
if (index != -1) {
val updatedTodoItem = currentList[index].copy(isCompleted = !currentList[index].isCompleted)
currentList[index] = updatedTodoItem
_todoItems.value = currentList
}
}
}
Loading