Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 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