Kotlin-Mvvm-DaggerHilt-RoomDatabase-NoteApp(Add,Update,Delete,DeleteAll)-Example

Kotlin-Mvvm-DaggerHilt-RoomDatabase-NoteApp(Add,Update,Delete,DeleteAll)-Example

Step 1:
Open build.gradle(:Project) and add following dependency and rebuild the project.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.40.5'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}
Step 2:
Open build.gradle(:app) and add following dependency and rebuild the project.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 31
defaultConfig {
applicationId "in.kotlinkatta.kotlinkattademo"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}

buildFeatures{
dataBinding true;
viewBinding true
}
}

dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//kotlin Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
//room
implementation "androidx.room:room-ktx:2.4.0"
implementation "androidx.room:room-runtime:2.4.0"
kapt "androidx.room:room-compiler:2.4.0"
//multidex
implementation 'androidx.multidex:multidex:2.0.1'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
// view model ktx
implementation 'androidx.activity:activity-ktx:1.4.0'
//hilt viewmodel
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0")
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//moshi
implementation("com.squareup.moshi:moshi-kotlin:1.12.0")
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
//SharedPreferences
implementation("androidx.datastore:datastore:1.0.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")
//DaggerHilt
implementation 'com.google.dagger:hilt-android:2.40.5'
kapt 'com.google.dagger:hilt-compiler:2.40.5'
}
// Allow references to generated code
kapt {
correctErrorTypes = true
}
Step 3:
Open gradle.properties and add following code and rebuild the project.
kapt.use.worker.api=false
Step 4:
Open AndroidManifest.xml and add following dependency and rebuild the project.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="in.kotlinkatta.kotlinkattademo">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".container.BaseApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KotlinKattaDemo">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
App Flow Image:
Step 5:
Create new class BaseApp.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.container

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class BaseApp : Application() {}
Step 6:
Create new data class NoteModel.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.model

import androidx.annotation.Keep
import androidx.room.Entity
import androidx.room.PrimaryKey

@Keep
@Entity(tableName = "notetable")
data class NoteModel(
val notetitle: String,
val notedesc: String
) {
@PrimaryKey(autoGenerate = true)
var tableid: Int = 0
}
Step 7:
Create new interface class NoteDao.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.dao

import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface NoteDao {
//Insert
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertNoteData(noteModel: NoteModel)

//Get Data
@Query("SELECT * FROM notetable")
fun getAllNoteData(): Flow<List<NoteModel>>

//UPDATE Data Using @Query
@Query("UPDATE notetable SET notetitle = :notetitle,notedesc = :notedesc WHERE tableid = :tableid")
suspend fun updateNoteDataByField(notetitle: String, notedesc: String, tableid: Int)

//DELETE Data Using @Query
@Query("DELETE FROM notetable WHERE tableid LIKE :tableid")
suspend fun deleteNoteDataByField(tableid: Int)

//DELETE Data Using @Query
@Query("DELETE FROM notetable")
suspend fun deleteAllNoteData()
}
Step 8:
Create new abstract class NoteDatabase.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.database

import `in`.kotlinkatta.kotlinkattademo.dao.NoteDao
import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [NoteModel::class], version = 1,exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {
abstract fun getNoteDao(): NoteDao
}
Step 9:
Create new object AppModule.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.di

import `in`.kotlinkatta.kotlinkattademo.dao.NoteDao
import `in`.kotlinkatta.kotlinkattademo.database.NoteDatabase
import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Provides
@Singleton
fun providesDatabase(@ApplicationContext context: Context): NoteDatabase =
Room.databaseBuilder(context, NoteDatabase::class.java, "NoteDatabase")
.build()

@Provides
fun providesPostsDataDao(noteDatabase: NoteDatabase): NoteDao =
noteDatabase.getNoteDao()
}
Step 10:
Create new class NoteRepository.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.repository

import `in`.kotlinkatta.kotlinkattademo.dao.NoteDao
import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import javax.inject.Inject

class NoteRepository @Inject constructor(
private val noteDao: NoteDao
) {
suspend fun insertNoteData(noteModel: NoteModel) =
withContext(Dispatchers.IO) {
noteDao.insertNoteData(noteModel)
}

val getAllNoteData: Flow<List<NoteModel>> = noteDao.getAllNoteData()

suspend fun updateNoteDataByField(notetitle: String, notedesc: String, tableid: Int) =
withContext(Dispatchers.IO) {
noteDao.updateNoteDataByField(notetitle, notedesc, tableid)
}

suspend fun deleteNoteDataByField(tableid: Int) =
withContext(Dispatchers.IO) {
noteDao.deleteNoteDataByField(tableid)
}

suspend fun deleteAllNoteData() =
withContext(Dispatchers.IO) {
noteDao.deleteAllNoteData()
}
}
Step 11:
Create new class NoteViewModel.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.uiviewmodel

import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import `in`.kotlinkatta.kotlinkattademo.repository.NoteRepository
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class NoteViewModel
@Inject
constructor(
private val noteRepository: NoteRepository
) : ViewModel() {

fun insertNoteData(noteModel: NoteModel) = viewModelScope.launch {
noteRepository.insertNoteData(noteModel)
}

val getAllNoteData: LiveData<List<NoteModel>> = noteRepository.getAllNoteData
.flowOn(Dispatchers.Main).asLiveData(context = viewModelScope.coroutineContext)

fun updateNoteDataByField(notetitle: String, notedesc: String, tableid: Int) =
viewModelScope.launch {
noteRepository.updateNoteDataByField(notetitle, notedesc, tableid)
}

fun deleteNoteDataByField(tableid: Int) =
viewModelScope.launch {
noteRepository.deleteNoteDataByField(tableid)
}

fun deleteAllNoteData() = viewModelScope.launch {
noteRepository.deleteAllNoteData()
}
}
Step 12:
Create new class NoteAdapter.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo.adapter

import `in`.kotlinkatta.kotlinkattademo.UpdateDeleteActivity
import `in`.kotlinkatta.kotlinkattademo.databinding.NoterowBinding
import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

public class NoteAdapter @Inject constructor(@ApplicationContext val appContext: Context) :
ListAdapter<NoteModel, NoteAdapter.NoteViewHolder>(
Diff
) {
inner class NoteViewHolder(val binding: NoterowBinding) : RecyclerView.ViewHolder(binding.root)

object Diff : DiffUtil.ItemCallback<NoteModel>() {
override fun areItemsTheSame(oldItem: NoteModel, newItem: NoteModel): Boolean {
return oldItem.tableid == newItem.tableid
}

override fun areContentsTheSame(oldItem: NoteModel, newItem: NoteModel): Boolean {
return oldItem == newItem
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
return NoteViewHolder(
NoterowBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}

override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
val postDataModel = getItem(position)
if (postDataModel != null) {
holder.binding.apply {
txttitle.text = postDataModel.notetitle
txtbody.text = postDataModel.notedesc
holder.binding.root.setOnClickListener {
val intentActivity = Intent(appContext, UpdateDeleteActivity::class.java)
intentActivity.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intentActivity.putExtra("note_table_id", postDataModel.tableid)
intentActivity.putExtra("note_title", postDataModel.notetitle)
intentActivity.putExtra("note_desc", postDataModel.notedesc)
appContext.startActivity(intentActivity)
}
}
}
}
}
Step 13:
Create new class noterow.xml and add following code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="5dp"
app:cardUseCompatPadding="true"
app:cardElevation="8dp"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:padding="5dp"
android:orientation="vertical"
android:layout_height="match_parent">
<TextView
android:id="@+id/txttitle"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="title"
android:textSize="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_marginTop="5dp"
android:id="@+id/txtbody"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="body"
android:textSize="13sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
Step 14:
Open Activity class MainActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo

import `in`.kotlinkatta.kotlinkattademo.adapter.NoteAdapter
import `in`.kotlinkatta.kotlinkattademo.databinding.ActivityMainBinding
import `in`.kotlinkatta.kotlinkattademo.model.NoteModel
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.NoteViewModel
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
public class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val noteViewModel: NoteViewModel by viewModels()

@Inject
lateinit var noteAdapter: NoteAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
initRecyclerView()

noteViewModel.getAllNoteData.observe(this, Observer { response ->
if (response.isEmpty()) {
binding.recyclerview.visibility = View.GONE
} else {
binding.recyclerview.visibility = View.VISIBLE
noteAdapter.submitList(response)
}
})

binding.btnDeleteAll.setOnClickListener {
noteViewModel.deleteAllNoteData()
}

binding.btnsubmit.setOnClickListener {
val strTitle = binding.edtitle.text.toString()
val strDesc = binding.eddesc.text.toString()
if (!TextUtils.isEmpty(strTitle) && !TextUtils.isEmpty(strDesc)) {
noteViewModel.insertNoteData(NoteModel(strTitle, strDesc))
binding.edtitle.text.clear()
binding.eddesc.text.clear()
} else {
Toast.makeText(this@MainActivity, "Enter All Field", Toast.LENGTH_SHORT).show()
}
}
}

private fun initRecyclerView() {
binding.apply {
recyclerview.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = noteAdapter
}
}
}
}
Step 15:
Open layout file activity_main.kt and add following code.
<?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">

<LinearLayout
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<LinearLayout
android:background="#FF03DAC5"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:gravity="center"
android:textColor="#FF000000"
android:textSize="20dp"
android:textStyle="bold"
android:text="Note App"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<androidx.appcompat.widget.AppCompatButton
android:background="#8BC34A"
android:id="@+id/btnDeleteAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete All"/>

</LinearLayout>

<EditText
android:id="@+id/edtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Note Title"/>

<EditText
android:id="@+id/eddesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Note Desc"/>

<androidx.appcompat.widget.AppCompatButton
android:background="#8BC34A"
android:id="@+id/btnsubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Submit"/>

<androidx.recyclerview.widget.RecyclerView
android:layout_marginTop="5dp"
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 16:
Create new Activity class UpdateDeleteActivity.kt and add following code.
package `in`.kotlinkatta.kotlinkattademo

import `in`.kotlinkatta.kotlinkattademo.databinding.ActivityUpdateDeleteBinding
import `in`.kotlinkatta.kotlinkattademo.uiviewmodel.NoteViewModel
import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class UpdateDeleteActivity : AppCompatActivity() {
private lateinit var binding: ActivityUpdateDeleteBinding
private val noteViewModel: NoteViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUpdateDeleteBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)

val i = intent
val strNoteTitle = i.getStringExtra("note_title")!!
val strNoteDesc = i.getStringExtra("note_desc")!!
val strNoteTableId: Int = i.getIntExtra("note_table_id", 0)

binding.edtitle.text = Editable.Factory.getInstance().newEditable(strNoteTitle)
binding.eddesc.text = Editable.Factory.getInstance().newEditable(strNoteDesc)

binding.btndelete.setOnClickListener {
noteViewModel.deleteNoteDataByField(strNoteTableId)
finish()
}

binding.btnupdate.setOnClickListener {
binding.apply {
val strTitle = edtitle.text.toString()
val strDesc = eddesc.text.toString()
if (!TextUtils.isEmpty(strTitle) && !TextUtils.isEmpty(strDesc)) {
noteViewModel.updateNoteDataByField(strTitle, strDesc, strNoteTableId)
edtitle.text.clear()
eddesc.text.clear()
finish()
} else {
Toast.makeText(this@UpdateDeleteActivity, "Enter All Field", Toast.LENGTH_SHORT)
.show()
}
}
}
}
}
Step 17:
Open layout file activity_update_delete.xml and add following code
<?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=".UpdateDeleteActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical">

<EditText
android:id="@+id/edtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Note Title" />

<EditText
android:id="@+id/eddesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Note Desc" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<androidx.appcompat.widget.AppCompatButton
android:layout_margin="5dp"
android:id="@+id/btnupdate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#8BC34A"
android:text="Update" />

<androidx.appcompat.widget.AppCompatButton
android:layout_margin="5dp"
android:id="@+id/btndelete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#8BC34A"
android:text="Delete" />

</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Output:




Comments