0% found this document useful (0 votes)
61 views

Codelab7 - SQLite

The document contains XML layout files and Kotlin code for an Android app that allows users to add words to a database using a RecyclerView. The layout files define the user interface for listing and adding words. The Kotlin code includes activities for the main screen and adding words, a Word data model, a WordDao for database access, and an adapter for the RecyclerView.

Uploaded by

Walter Peralta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views

Codelab7 - SQLite

The document contains XML layout files and Kotlin code for an Android app that allows users to add words to a database using a RecyclerView. The layout files define the user interface for listing and adding words. The Kotlin code includes activities for the main screen and adding words, a Word data model, a WordDao for database access, and an adapter for the RecyclerView.

Uploaded by

Walter Peralta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 11

Peralta, Walter V.

CC17 CITCS 3B-2


XML files
activitymain.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.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>

<!-- Base application theme. -->


<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

<!-- The default font for RecyclerView items is too small.


The margin is a simple delimiter between the words. -->
<style name="word_title">
<item name="android:layout_marginBottom">8dp</item>
<item name="android:paddingLeft">8dp</item>
<item name="android:background">@android:color/holo_orange_light</item>
<item
name="android:textAppearance">@android:style/TextAppearance.Large</item>
</style>
</resources>
Kotlin codes
MainActivity.kt
package com.example.android.roomwordssample

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

class MainActivity : AppCompatActivity() {

private val newWordActivityRequestCode = 1


private val wordViewModel: WordViewModel by viewModels {
WordViewModelFactory((application as WordsApplication).repository)
}

override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)


val adapter = WordListAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
wordViewModel.allWords.observe(owner = this) { words ->

words.let { adapter.submitList(it) }
}

val fab = findViewById<FloatingActionButton>(R.id.fab)


fab.setOnClickListener {
val intent = Intent(this@MainActivity, NewWordActivity::class.java)
startActivityForResult(intent, newWordActivityRequestCode)
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, intentData:


Intent?) {
super.onActivityResult(requestCode, resultCode, intentData)

if (requestCode == newWordActivityRequestCode && resultCode ==


Activity.RESULT_OK) {
intentData?.getStringExtra(NewWordActivity.EXTRA_REPLY)?.let { reply ->
val word = Word(reply)
wordViewModel.insert(word)
}
} else {
Toast.makeText(
applicationContext,
R.string.empty_not_saved,
Toast.LENGTH_LONG
).show()
}
}
}
NewWordActivity.kt

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

class NewWordActivity : AppCompatActivity() {

public override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_word)
val editWordView = findViewById<EditText>(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"
}
}
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)

@Query("DELETE FROM word_table")


suspend fun deleteAll()
}
WordListAdapter.kt
package com.example.android.roomwordssample

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

class WordListAdapter : ListAdapter<Word, WordViewHolder>(WORDS_COMPARATOR) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder


{
return WordViewHolder.create(parent)
}
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current.word)
}

class WordViewHolder(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): 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
}

override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {


return oldItem.word == newItem.word
}
}
}
}
WordRepository.kt
package com.example.android.roomwordssample

import androidx.annotation.WorkerThread
import kotlinx.coroutines.flow.Flow

class WordRepository(private val wordDao: WordDao) {

val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()

@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

@Database(entities = [Word::class], version = 1)


abstract class WordRoomDatabase : RoomDatabase() {

abstract fun wordDao(): WordDao

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
}
}

private class WordDatabaseCallback(


private val scope: CoroutineScope
) : RoomDatabase.Callback() {

override fun onCreate(db: SupportSQLiteDatabase) {


super.onCreate(db)
// If you want to keep the data through app restarts,
// comment out the following line.
INSTANCE?.let { database ->
scope.launch(Dispatchers.IO) {
populateDatabase(database.wordDao())
}
}
}
}

suspend fun populateDatabase(wordDao: WordDao) {


// Start the app with a clean database every time.
// Not needed if you only populate on creation.
wordDao.deleteAll()

var word = Word("Hello")


wordDao.insert(word)
word = Word("World!")
wordDao.insert(word)
}
}
}
WordsApplication.kt
package com.example.android.roomwordssample

import android.app.Application
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob

class WordsApplication : Application() {

val applicationScope = CoroutineScope(SupervisorJob())

val database by lazy { WordRoomDatabase.getDatabase(this, applicationScope) }


val repository by lazy { WordRepository(database.wordDao()) }
}
WordViewModel.kt
package com.example.android.roomwordssample

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class WordViewModel(private val repository: WordRepository) : ViewModel() {


val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()

fun insert(word: Word) = viewModelScope.launch {


repository.insert(word)
}
}

class WordViewModelFactory(private val repository: WordRepository) :


ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(WordViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return WordViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
WordDaoTest.kt
package com.example.android.roomwordssample

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 {

private lateinit var wordDao: WordDao


private lateinit var db: WordRoomDatabase

@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

You might also like