Created
September 6, 2023 09:48
-
-
Save amoozeshbebin/ab80f0dad7fd9d4db42c95d3c571d66d to your computer and use it in GitHub Desktop.
ToDo App with Database
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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