Skip to content

Instantly share code, notes, and snippets.

@amoozeshbebin
Created September 6, 2023 09:48
Show Gist options
  • Save amoozeshbebin/ab80f0dad7fd9d4db42c95d3c571d66d to your computer and use it in GitHub Desktop.
Save amoozeshbebin/ab80f0dad7fd9d4db42c95d3c571d66d to your computer and use it in GitHub Desktop.
ToDo App with Database
MainActivity.kt:
package com.example.todoapp
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.example.todoapp.adapters.RecyclerAdapter
import com.example.todoapp.handler.SwipeToDeleteCallback
import com.example.todoapp.models.ToDo
import com.example.todoapp.viewmodels.ToDoViewModel
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter:RecyclerAdapter
private lateinit var toDoViewModel: ToDoViewModel
private lateinit var fab:FloatingActionButton
private lateinit var swipe:SwipeRefreshLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
}
private fun init(){
bindViews()
val layoutManager= LinearLayoutManager(this)
recyclerView.layoutManager=layoutManager
adapter= RecyclerAdapter(this)
recyclerView.adapter=adapter
toDoViewModel= ViewModelProvider(this)[ToDoViewModel::class.java]
toDoViewModel.getAllData().observe(this){
adapter.dataSetList(it)
}
//adding from 50 ta 65
val addNewToDoResultActivity=
registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result->
if(result.resultCode==Activity.RESULT_OK){
val data=result.data ?: return@registerForActivityResult
val title=data.getStringExtra(AddOrEditToDoActivity.EXTRA_TITLE)
val description=data.getStringExtra(AddOrEditToDoActivity.EXTRA_DESC)
val toDo=ToDo(title=title!!,description=description!!, done = false)
MainScope().launch { toDoViewModel.insert(toDo) }
Toast.makeText(this,"new todo added",Toast.LENGTH_SHORT).show()
}
}
fab.setOnClickListener{
val intent=Intent(this,AddOrEditToDoActivity::class.java)
addNewToDoResultActivity.launch(intent)
}
swipe.setOnRefreshListener {
adapter.notifyDataSetChanged()
swipe.isRefreshing=false
}
val swipeHandler=object :SwipeToDeleteCallback(this){
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
MainScope().launch {
toDoViewModel.delete(adapter.getToDo(viewHolder.adapterPosition))
Toast.makeText(this@MainActivity, "todo item deleted", Toast.LENGTH_SHORT)
.show()
}
}
}
val itemTouchHelper=ItemTouchHelper(swipeHandler)
itemTouchHelper.attachToRecyclerView(recyclerView)
// editing from 83 ta 108
val editToDoResultActivity=
registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result->
if(result.resultCode==Activity.RESULT_OK){
val data=result.data ?: return@registerForActivityResult
val id=data.getIntExtra(AddOrEditToDoActivity.EXTRA_ID,-1)
if(id == -1){
Toast.makeText(this,"invalid data",Toast.LENGTH_SHORT).show()
return@registerForActivityResult
}
val title=data.getStringExtra(AddOrEditToDoActivity.EXTRA_TITLE)
val description=data.getStringExtra(AddOrEditToDoActivity.EXTRA_DESC)
val toDo=ToDo(title=title!!,description=description!!, done = false,id=id)
MainScope().launch { toDoViewModel.update(toDo) }
Toast.makeText(this," todo updated",Toast.LENGTH_SHORT).show()
}
}
adapter.setOnItemClickListener(object :RecyclerAdapter.OnItemClickListener{
override fun onItemClick(toDo: ToDo) {
val intent=Intent(this@MainActivity,AddOrEditToDoActivity::class.java)
intent.putExtra(AddOrEditToDoActivity.EXTRA_TITLE,toDo.title)
intent.putExtra(AddOrEditToDoActivity.EXTRA_DESC,toDo.description)
intent.putExtra(AddOrEditToDoActivity.EXTRA_ID,toDo.id)
editToDoResultActivity.launch(intent)
}
})
adapter.setOnDoneStateChangeListener(object :RecyclerAdapter.OnDoneStateChangeListener{
override fun onStateChanged(toDo: ToDo, isChecked: Boolean) {
toDo.done=isChecked
MainScope().launch { toDoViewModel.update(toDo) }
}
})
}
private fun bindViews(){
recyclerView=findViewById(R.id.recyclerView)
fab=findViewById(R.id.fab)
swipe=findViewById(R.id.swipe)
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:contentDescription="@string/app_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@android:drawable/ic_input_add" />
</androidx.constraintlayout.widget.ConstraintLayout>
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
AddOrEditToDoActivity.kt:
package com.example.todoapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
class AddOrEditToDoActivity : AppCompatActivity() {
companion object{
const val EXTRA_ID="EXTRA_ID"
const val EXTRA_TITLE="EXTRA_TITLE"
const val EXTRA_DESC="EXTRA_DESC"
}
private lateinit var txtTitle:EditText
private lateinit var txtDescription:EditText
private lateinit var btnAdd:Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_to_do)
init()
}
private fun init(){
bindViews()
val inputIntent=Intent()
if(inputIntent.hasExtra(EXTRA_ID)){
title="edit todo"
txtTitle.setText(inputIntent.getStringExtra(EXTRA_TITLE))
txtDescription.setText(inputIntent.getStringExtra(EXTRA_DESC))
btnAdd.text="update item"
}else{
title="add todo"
btnAdd.text="add new item"
}
btnAdd.setOnClickListener {
val title=txtTitle.text.toString()
val description=txtDescription.text.toString()
if(title=="" || description==""){
Toast.makeText(this,"fil tile and description",Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val data= Intent()
data.putExtra(EXTRA_TITLE,title)
data.putExtra(EXTRA_DESC,description)
val id=intent.extras?.getInt(EXTRA_ID,-1)
if(id != -1){
data.putExtra(EXTRA_ID,id)
}
setResult(RESULT_OK,data)
finish()
}
}
private fun bindViews(){
txtTitle=findViewById(R.id.txtTitle)
txtDescription=findViewById(R.id.txtDescription)
btnAdd=findViewById(R.id.btnAdd)
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
ToDoViewModel.kt:
package com.example.todoapp.viewmodels
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import com.example.todoapp.models.ToDo
import com.example.todoapp.repositories.ToDoRepository
class ToDoViewModel(application: Application) :AndroidViewModel(application){
private var repository:ToDoRepository= ToDoRepository(application)
private var allToDoList=repository.getAllToDos()
suspend fun insert(toDo:ToDo){
repository.insertToDo(toDo)
}
suspend fun update(toDo:ToDo){
if(toDo.id <=0) return
repository.updateToDo(toDo)
}
suspend fun delete(toDo: ToDo){
if(toDo.id <=0) return
repository.deleteToDo(toDo)
}
fun getAllData(): LiveData<List<ToDo>> {
return allToDoList
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
ToDoRepository.kt:
package com.example.todoapp.repositories
import android.app.Application
import androidx.lifecycle.LiveData
import com.example.todoapp.dao.ToDoDao
import com.example.todoapp.db.ToDoDatabase
import com.example.todoapp.models.ToDo
class ToDoRepository(application: Application) {
private var toDoDao:ToDoDao
private var allToDoList:LiveData<List<ToDo>>
init{
val database=ToDoDatabase.getInstance(application)
toDoDao= database!!.toDoDao()
allToDoList= toDoDao.getAllToDos()
}
suspend fun insertToDo(toDo: ToDo){
toDoDao.insertToDo(toDo)
}
suspend fun updateToDo(toDo: ToDo){
toDoDao.updateToDo(toDo)
}
suspend fun deleteToDo(toDo: ToDo){
toDoDao.deleteToDo(toDo)
}
fun getAllToDos(): LiveData<List<ToDo>> {
return allToDoList
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
ToDo.kt:
package com.example.todoapp.models
import android.widget.Button
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
data class ToDo(
@PrimaryKey(autoGenerate = true) val id:Int=0,
var title:String,
var description:String,
var done:Boolean
)
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
SwipToDeleteCallBack:
package com.example.todoapp.handler
import android.content.Context
import android.widget.Toast
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.adapters.RecyclerAdapter
import com.example.todoapp.viewmodels.ToDoViewModel
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
open class SwipeToDeleteCallback(val context: Context):
ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
TODO("Not yet implemented")
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
ToDoDatabase.kt:
package com.example.todoapp.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.todoapp.dao.ToDoDao
import com.example.todoapp.models.ToDo
@Database(entities = [ToDo::class], version = 2)
abstract class ToDoDatabase:RoomDatabase() {
abstract fun toDoDao():ToDoDao
companion object{
private var instance: ToDoDatabase? =null
fun getInstance(context: Context): ToDoDatabase? {
if(instance==null){
instance= Room.databaseBuilder(
context,
ToDoDatabase::class.java,"todo.db"
).fallbackToDestructiveMigration().build()
}
return instance
}
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
ToDoDao.abstract:
package com.example.todoapp.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import com.example.todoapp.models.ToDo
@Dao
interface ToDoDao {
@Insert
suspend fun insertToDo(todo:ToDo)
@Update
suspend fun updateToDo(todo: ToDo)
@Delete
suspend fun deleteToDo(todo: ToDo)
@Query(" SELECT * FROM todo ")
fun getAllToDos() :LiveData<List<ToDo>>
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
RecyclerAdapter.kt:
package com.example.todoapp.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.R
import com.example.todoapp.models.ToDo
class RecyclerAdapter(
private val context: Context):
RecyclerView.Adapter<RecyclerAdapter.ToDoHolder>(){
private val dataList=ArrayList<ToDo>()
private lateinit var onItemClickListener:OnItemClickListener
private lateinit var onDoneStateChangeListener:OnDoneStateChangeListener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ToDoHolder {
val inflate= LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item_layout,parent,false)
return ToDoHolder(inflate)
}
override fun onBindViewHolder(holder: ToDoHolder, position: Int) {
val item=dataList[position]
holder.checkbox.text=item.title
holder.checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
onDoneStateChangeListener.onStateChanged(item,isChecked)
}
}
override fun getItemCount(): Int {
return dataList.size
}
fun dataSetList(list:List<ToDo>){
dataList.clear()
dataList.addAll(list)
notifyDataSetChanged()
}
fun getToDo(position:Int): ToDo {
return dataList[position]
}
inner class ToDoHolder(v: View) : RecyclerView.ViewHolder(v) {
var checkbox:CheckBox =v.findViewById(R.id.chx)
init {
v.setOnClickListener{
onItemClickListener.onItemClick(dataList[adapterPosition])
}
}
}
interface OnItemClickListener{
fun onItemClick(toDo: ToDo)
}
fun setOnItemClickListener(onItemClickListener:OnItemClickListener){
this.onItemClickListener=onItemClickListener
}
interface OnDoneStateChangeListener{
fun onStateChanged(toDo: ToDo, isChecked: Boolean)
}
fun setOnDoneStateChangeListener(onDoneStateChangeListener:OnDoneStateChangeListener){
this.onDoneStateChangeListener=onDoneStateChangeListener
}
}
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
todo_item_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:elevation="8dp"
app:cardCornerRadius="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/chx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="todo item"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
activity_add_todo.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="25dp"
tools:context=".AddOrEditToDoActivity">
<EditText
android:id="@+id/txtTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:ems="10"
android:hint="title"
android:inputType="textPersonName"
android:minHeight="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/txtDescription"
android:layout_width="344dp"
android:layout_height="205dp"
android:layout_marginTop="44dp"
android:ems="10"
android:hint="description"
android:inputType="textPersonName"
android:minHeight="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtTitle" />
<Button
android:id="@+id/btnAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="108dp"
android:text="add todo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.526"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment