Codelab7 - SQLite
Codelab7 - SQLite
<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.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="0dp"
android:layout_height="0dp"
tools:listitem="@layout/recyclerview_item"
android:padding="@dimen/big_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/add_word"
android:src="@drawable/ic_add_black_24dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
activity_new_word.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edit_word"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/min_height"
android:fontFamily="sans-serif-light"
android:hint="@string/hint_word"
android:inputType="textAutoComplete"
android:layout_margin="@dimen/big_padding"
android:textSize="18sp" />
<Button
android:id="@+id/button_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:text="@string/button_save"
android:layout_margin="@dimen/big_padding"
android:textColor="@color/buttonLabel" />
</LinearLayout>
content_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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.android.roomwordssample.MainActivity"
tools:showIn="@layout/activity_main">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/recyclerview_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>
recyclerviewer_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
style="@style/word_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light" />
</LinearLayout>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="buttonLabel">#FFFFFF</color>
</resources>
dimens.xml
<resources>
<dimen name="small_padding">8dp</dimen>
<dimen name="big_padding">16dp</dimen>
<dimen name="min_height">48dp</dimen>
</resources>
strings.xml
<resources>
<string name="app_name">RoomWordsSample</string>
<string name="action_settings">Settings</string>
<string name="hint_word">Word…</string>
<string name="button_save">Save</string>
<string name="empty_not_saved">Word not saved because it is empty.</string>
<string name="add_word">Add word</string>
</resources>
styles.xml
<resources>
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
words.let { adapter.submitList(it) }
}
package com.example.android.roomwordssample
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
companion object {
const val EXTRA_REPLY = "com.example.android.wordlistsql.REPLY"
}
}
Word.kt
package com.example.android.roomwordssample
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "word_table")
data class Word(@PrimaryKey @ColumnInfo(name = "word") val word: String)
WordDao.kt
package com.example.android.roomwordssample
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface WordDao {
// The flow always holds/caches latest version of data. Notifies its observers
when the
// data has changed.
@Query("SELECT * FROM word_table ORDER BY word ASC")
fun getAlphabetizedWords(): Flow<List<Word>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(word: Word)
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
import com.example.android.roomwordssample.WordListAdapter.WordViewHolder
companion object {
fun create(parent: ViewGroup): WordViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item, parent, false)
return WordViewHolder(view)
}
}
}
companion object {
private val WORDS_COMPARATOR = object : DiffUtil.ItemCallback<Word>() {
override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {
return oldItem === newItem
}
import androidx.annotation.WorkerThread
import kotlinx.coroutines.flow.Flow
@Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun insert(word: Word) {
wordDao.insert(word)
}
}
WordRoomDatabase.kt
package com.example.android.roomwordssample
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.Dispatchers
import kotlinx.coroutines.launch
companion object {
@Volatile
private var INSTANCE: WordRoomDatabase? = null
fun getDatabase(
context: Context,
scope: CoroutineScope
): WordRoomDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
WordRoomDatabase::class.java,
"word_database"
)
.fallbackToDestructiveMigration()
.addCallback(WordDatabaseCallback(scope))
.build()
INSTANCE = instance
instance
}
}
import android.app.Application
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
@RunWith(AndroidJUnit4::class)
class WordDaoTest {
@Before
fun createDb() {
val context: Context = ApplicationProvider.getApplicationContext()
// Using an in-memory database because the information stored here disappears
when the
// process is killed.
db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
wordDao = db.wordDao()
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun insertAndGetWord() = runBlocking {
val word = Word("word")
wordDao.insert(word)
val allWords = wordDao.getAlphabetizedWords().first()
assertEquals(allWords[0].word, word.word)
}
@Test
@Throws(Exception::class)
fun getAllWords() = runBlocking {
val word = Word("aaa")
wordDao.insert(word)
val word2 = Word("bbb")
wordDao.insert(word2)
val allWords = wordDao.getAlphabetizedWords().first()
assertEquals(allWords[0].word, word.word)
assertEquals(allWords[1].word, word2.word)
}
@Test
@Throws(Exception::class)
fun deleteAll() = runBlocking {
val word = Word("word")
wordDao.insert(word)
val word2 = Word("word2")
wordDao.insert(word2)
wordDao.deleteAll()
val allWords = wordDao.getAlphabetizedWords().first()
assertTrue(allWords.isEmpty())
}
}
Output