Download the Political Preparedness Project Starter Code
Inside the database package you have the following files:
- Converters.kt
- ElectionDao.kt
ElectionDatabase.kt
ElectionsLocalRepository.kt
Converters.kt
A converter as per Cambridge English dictionary is a machine or device that changes something into a different form.
In android, room doesn't allow for object references between entities . In this app we need to store a custom data type in a single database column. We support custom types by providing type converters, which are methods that tell Room how to convert custom types to and from known types that Room can persist.
Read more on Referencing complex data using Room
import androidx.room.TypeConverter
import java.util.*
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time?.toLong()
}
}
Check out Converters.kt code on GitHub
ElectionDatabase.kt
You add the @ TypeConverters annotation to the AppDatabase class so that Room knows about the converter class that you have defined:
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract val electionDao: ElectionDao
}
Check out ElectionDatabase.kt code on GitHub
ElectionDao.kt
When you use the Room persistence library to store your app's data, you interact with the stored data by defining data access objects (DAOs). Each DAO includes methods that offer abstract access to your app's database.
You can define each DAO as either an interface or an abstract class- always annotate your DAOs with @ Dao
The DAO methods that define database interactions are divided into two:
Convenience methods that let you insert, update, and delete rows in your database without writing any SQL code.
Query methods that let you write your own SQL query to interact with the database.
Read more on accessing data using Room DAOs
import androidx.lifecycle.LiveData
import androidx.room.*
import com.example.android.politicalpreparedness.network.models.Election
@Dao
interface ElectionDao {
//inserting all eletions
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun allElectionsInserted(vararg elections: Election)
// Add select all election query
@Query("SELECT * from election_table" )
fun getElections(): LiveData<List<Election>>
@Query("SELECT * FROM election_table WHERE id in (SELECT id FROM elections_followed_table) ORDER BY electionDay DESC")
fun getElectionsFollowed(): LiveData<List<Election>>
@Query("SELECT CASE id WHEN NULL THEN 0 ELSE 1 END FROM elections_followed_table WHERE id = :electionSavedId")
fun isElectionSaved(electionSavedId: Int): LiveData<Int>
@Query("INSERT INTO elections_followed_table (id) VALUES(:electionFollowedId)")
suspend fun electionFollowed(electionFollowedId: Int)
suspend fun electionFollowed(election: Election){
electionFollowed(election.id)
}
@Query("DELETE FROM elections_followed_table WHERE id = :electionUnFollowId")
suspend fun electionUnFollow(electionUnFollowId: Int)
suspend fun electionUnFollow(election: Election){
electionUnFollow(election.id)
}
}
Check out ElectionDao.kt code on GitHub
Live Data
An observable data holder class that is lifecycle aware.
LiveData allows UI controller observers to subscribe to updates. When the data held by the LiveData object changes, the UI automatically updates in response.
The Room persistence library supports observable queries, which return LiveData objects. Observable queries are written as part of a Database Access Object (DAO).
Learn more on LiveData
ElectionsLocalRepository.kt
Repository modules handle data operations. They provide a clean API so that the rest of the app can retrieve this data easily. They know where to get the data from and what API calls to make when data is updated. They can be considered to be mediators between different data sources, such as persistent models, web services, and caches.
import android.content.ContentValues
import android.util.Log
import androidx.lifecycle.LiveData
import com.example.android.politicalpreparedness.network.CivicsApi
import com.example.android.politicalpreparedness.network.models.Election
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class ElectionsLocalRepository(private val electionDatabase: ElectionDatabase
){
//list of elections tha has been saved
val elections:LiveData<List<Election>> =electionDatabase.electionDao.getElections()
// The list of elections that has been followed.
// The list of followed elections.
val electionsFollowed: LiveData<List<Election>> = electionDatabase.electionDao.getElectionsFollowed()
suspend fun electionsRefreshed() {
withContext(Dispatchers.IO) {
try {
// Get String Json response via Retrofit
val electionsResponse = CivicsApi.retrofitService.electionResponse()
val result = electionsResponse.elections
// Push the results to the database
electionDatabase.electionDao.allElectionsInserted(*result.toTypedArray())
Log.d(ContentValues.TAG, result.toString())
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
Check out ElectionsLocalRepository.kt code on GitHub
Read more on Guide to app architecture
Many thanks for reading see you in part 3