AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.roomtest">
<application
android:name=".DeptApplication"
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.RoomTest">
<activity android:name=".NewDept"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
build.gradle
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.roomtest"
minSdkVersion 21
targetSdkVersion 30
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
}
packagingOptions {
exclude 'META-INF/atomicfu.kotlin_module'
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// Room
implementation "androidx.room:room-ktx:$rootProject.roomVersion"
implementation 'androidx.room:room-runtime:2.2.5'
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion"
// activity components
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
}
src / Dept.kt
package com.example.roomtest
import androidx.room.*
import kotlinx.coroutines.flow.Flow
// Entity
@Entity(tableName = "departements")
class Dept(
@PrimaryKey @ColumnInfo(name = "name") val name: String
)
src / DeptApplication.kt
package com.example.roomtest
import android.app.Application
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
class DeptApplication : Application(){
val applicationScope = CoroutineScope(SupervisorJob())
val database by lazy { DeptDatabase.getDatabase(this, applicationScope) }
val deptDao by lazy { database.deptDao() }
}
src / DeptDAO.kt
package com.example.roomtest
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface DeptDAO {
@Query("SELECT * FROM departements ORDER BY name ASC")
fun getDeptNames(): Flow<List<Dept>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(dept: Dept)
@Query("DELETE FROM departements")
suspend fun deleteAll()
}
src / DeptDatabase.kt
package com.example.roomtest
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlin.reflect.KParameter
@Database(entities = arrayOf(Dept::class), version = 1, exportSchema = false)
public abstract class DeptDatabase : RoomDatabase() {
abstract fun deptDao(): DeptDAO
companion object {
@Volatile
private var INSTANCE: DeptDatabase? = null
fun getDatabase(
context: Context,
scope: CoroutineScope
): DeptDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
DeptDatabase::class.java,
"dept_database"
).addCallback(DeptDatabaseCallback(scope)).build()
INSTANCE = instance
instance
}
}
}
private class DeptDatabaseCallback(private val scope:
CoroutineScope
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.deptDao())
}
}
}
suspend fun populateDatabase(deptDao: DeptDAO) {
deptDao.deleteAll()
var tab = arrayOf("MEA", "IG", "STE", "M&I", "Mat", "STIA")
var dept: Dept
for (name in tab) {
dept = Dept(name)
deptDao.insert(dept)
}
}
}
}
src / DeptListAdapter.kt
package com.example.roomtest
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
class DeptListAdapter : ListAdapter<Dept, DeptListAdapter.
DeptViewHolder>(DeptsComparator()) {
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): DeptViewHolder {
return DeptViewHolder.create(parent)
}
override fun onBindViewHolder(holder: DeptViewHolder,
position: Int) {
val current = getItem(position)
holder.bind(current.name)
}
class DeptViewHolder(itemView: View) : RecyclerView.
ViewHolder(itemView) {
private val wordItemView: TextView = itemView.
findViewById(R.id.textView)
fun bind(text: String?) {
wordItemView.text = text
}
companion object {
fun create(parent: ViewGroup): DeptViewHolder { val view: View = LayoutInflater.from(parent
.context)
.inflate(R.layout.recyclerview_item,
parent, false)
return DeptViewHolder(view)
}
}
}
class DeptsComparator : DiffUtil.ItemCallback<Dept>() {
override fun areItemsTheSame(oldItem: Dept, newItem
: Dept): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Dept,
newItem: Dept): Boolean {
return oldItem.name == newItem.name
}
}
}
src / DeptViewModel.kt
package com.example.roomtest
import androidx.lifecycle.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class DeptViewModel(private val dao: DeptDAO) : ViewModel() {
val depts: Flow<List<Dept>> = dao.getDeptNames()
val allDepts: LiveData<List<Dept>> = this.depts.asLiveData()
fun insert(dept: Dept) = viewModelScope.launch {
dao.insert(dept)
}
}
class DeptViewModelFactory(private val dao: DeptDAO) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(DeptViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return DeptViewModel(dao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
src / MainActivity.kt
package com.example.roomtest
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.flow.count
class MainActivity : AppCompatActivity() {
private val newDeptActivityRequestCode = 1
private val deptViewModel: DeptViewModel by viewModels {
DeptViewModelFactory(
(application as DeptApplication).deptDao
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.deptslist)
val adapter = DeptListAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
deptViewModel.allDepts.observe(this) {
depts -> depts.let { adapter.submitList(it) }
}
val add =
findViewById<FloatingActionButton>(R.id.addButton)
add.setOnClickListener {
val intent = Intent(
this@MainActivity, NewDept::class.java)
startActivityForResult(
intent, newDeptActivityRequestCode)
}
val dept = Dept("new")
deptViewModel.insert(dept)
}
override fun onActivityResult(requestCode: Int,
resultCode: Int, intentData: Intent?) {
super.onActivityResult(
requestCode, resultCode, intentData)
if(requestCode == newDeptActivityRequestCode && resultCode == Activity.RESULT_OK) {
intentData?.getStringExtra(NewDept.EXTRA_REPLY)?.let{ reply ->
val dept = Dept(reply)
deptViewModel.insert(dept)
}
}
else {
Toast.makeText(
applicationContext,
"chaîne vide, aucun ajout n'est fait",
Toast.LENGTH_LONG
).show()
}
}
}
src / NewDept.kt
package com.example.roomtest
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.widget.Button
import android.widget.EditText
class NewDept : AppCompatActivity() {
private lateinit var editWordView: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_dept)
editWordView = findViewById(R.id.edit_word)
val button = findViewById<Button>(R.id.button_save)
button.setOnClickListener {
val replyIntent = Intent()
if (TextUtils.isEmpty(editWordView.text)) {
setResult(Activity.RESULT_CANCELED, replyIntent)
} else {
val word = editWordView.text.toString()
replyIntent.putExtra(EXTRA_REPLY, word)
setResult(Activity.RESULT_OK, replyIntent)
}
finish()
}
}
companion object {
const val EXTRA_REPLY = "com.example.android.wordlistsql.REPLY"
}
}