Started working on Cache Database
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.library'
|
id 'com.android.library'
|
||||||
id 'org.jetbrains.kotlin.android'
|
id 'org.jetbrains.kotlin.android'
|
||||||
|
id 'io.realm.kotlin'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -46,6 +47,9 @@ dependencies {
|
|||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
implementation 'com.google.code.gson:gson:2.9.0'
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
|
|
||||||
|
// Realm
|
||||||
|
implementation 'io.realm.kotlin:library-base:1.10.0'
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
|
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
|
||||||
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.7.3', ext: 'pom'
|
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.7.3', ext: 'pom'
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package net.buzzert.kordophone.backend.db
|
||||||
|
|
||||||
|
import io.realm.kotlin.Realm
|
||||||
|
import io.realm.kotlin.RealmConfiguration
|
||||||
|
import net.buzzert.kordophone.backend.db.model.Conversation
|
||||||
|
import net.buzzert.kordophone.backend.db.model.toDatabaseConversation
|
||||||
|
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
|
||||||
|
|
||||||
|
internal class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
||||||
|
companion object {
|
||||||
|
private val schema = setOf(Conversation::class)
|
||||||
|
|
||||||
|
fun liveDatabase(): CachedChatDatabase {
|
||||||
|
return CachedChatDatabase(
|
||||||
|
RealmConfiguration.Builder(schema = schema)
|
||||||
|
.name("chat-cache")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testDatabase(): CachedChatDatabase {
|
||||||
|
return CachedChatDatabase(
|
||||||
|
RealmConfiguration.Builder(schema = schema)
|
||||||
|
.name("chat-cache-test")
|
||||||
|
.inMemory()
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val realm = Realm.open(realmConfig)
|
||||||
|
|
||||||
|
fun writeConversations(conversations: List<ModelConversation>) {
|
||||||
|
val dbConversations = conversations.map { it.toDatabaseConversation() }
|
||||||
|
realm.writeBlocking {
|
||||||
|
dbConversations.forEach {
|
||||||
|
copyToRealm(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchConversations(): List<ModelConversation> {
|
||||||
|
val items = realm.query(Conversation::class).find()
|
||||||
|
return items.map { it.toConversation() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package net.buzzert.kordophone.backend.db.model
|
||||||
|
|
||||||
|
import io.realm.kotlin.Realm
|
||||||
|
import io.realm.kotlin.ext.realmListOf
|
||||||
|
import io.realm.kotlin.ext.toRealmList
|
||||||
|
import io.realm.kotlin.types.RealmInstant
|
||||||
|
import io.realm.kotlin.types.RealmList
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
|
import io.realm.kotlin.types.annotations.PrimaryKey
|
||||||
|
import net.buzzert.kordophone.backend.model.GUID
|
||||||
|
import org.mongodb.kbson.ObjectId
|
||||||
|
import java.time.Instant
|
||||||
|
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
open class Conversation(
|
||||||
|
@PrimaryKey
|
||||||
|
var _id: String,
|
||||||
|
|
||||||
|
var displayName: String?,
|
||||||
|
var participants: RealmList<String>,
|
||||||
|
var date: RealmInstant,
|
||||||
|
var unreadCount: Int,
|
||||||
|
var lastMessagePreview: String,
|
||||||
|
var guid: GUID,
|
||||||
|
|
||||||
|
// TODO: Not sure how to do this yet...
|
||||||
|
// var messages: RealmList<Message>,
|
||||||
|
): RealmObject
|
||||||
|
{
|
||||||
|
constructor(): this(
|
||||||
|
_id = ObjectId().toString(),
|
||||||
|
displayName = null,
|
||||||
|
participants = realmListOf(),
|
||||||
|
date = RealmInstant.now(),
|
||||||
|
unreadCount = 0,
|
||||||
|
lastMessagePreview = "",
|
||||||
|
guid = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toConversation(): ModelConversation {
|
||||||
|
return ModelConversation(
|
||||||
|
displayName = displayName,
|
||||||
|
participants = participants!!.toList(),
|
||||||
|
date = Date.from(date.toInstant()),
|
||||||
|
unreadCount = unreadCount,
|
||||||
|
guid = guid,
|
||||||
|
lastMessagePreview = lastMessagePreview
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ModelConversation.toDatabaseConversation(): Conversation {
|
||||||
|
val from = this
|
||||||
|
return Conversation().apply {
|
||||||
|
displayName = from.displayName
|
||||||
|
participants = from.participants.toRealmList()
|
||||||
|
date = from.date.toInstant().toRealmInstant()
|
||||||
|
unreadCount = from.unreadCount
|
||||||
|
lastMessagePreview = from.lastMessagePreview
|
||||||
|
guid = from.guid
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package net.buzzert.kordophone.backend.db.model
|
||||||
|
|
||||||
|
import io.realm.kotlin.types.RealmInstant
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
// Copied from Realm's documentation
|
||||||
|
// https://www.mongodb.com/docs/realm/sdk/kotlin/realm-database/schemas/supported-types/
|
||||||
|
|
||||||
|
fun RealmInstant.toInstant(): Instant {
|
||||||
|
val sec: Long = this.epochSeconds
|
||||||
|
// The value always lies in the range `-999_999_999..999_999_999`.
|
||||||
|
// minus for timestamps before epoch, positive for after
|
||||||
|
val nano: Int = this.nanosecondsOfSecond
|
||||||
|
return if (sec >= 0) { // For positive timestamps, conversion can happen directly
|
||||||
|
Instant.ofEpochSecond(sec, nano.toLong())
|
||||||
|
} else {
|
||||||
|
// For negative timestamps, RealmInstant starts from the higher value with negative
|
||||||
|
// nanoseconds, while Instant starts from the lower value with positive nanoseconds
|
||||||
|
// TODO This probably breaks at edge cases like MIN/MAX
|
||||||
|
Instant.ofEpochSecond(sec - 1, 1_000_000 + nano.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Instant.toRealmInstant(): RealmInstant {
|
||||||
|
val sec: Long = this.epochSecond
|
||||||
|
// The value is always positive and lies in the range `0..999_999_999`.
|
||||||
|
val nano: Int = this.nano
|
||||||
|
|
||||||
|
return if (sec >= 0) { // For positive timestamps, conversion can happen directly
|
||||||
|
RealmInstant.from(sec, nano)
|
||||||
|
} else {
|
||||||
|
// For negative timestamps, RealmInstant starts from the higher value with negative
|
||||||
|
// nanoseconds, while Instant starts from the lower value with positive nanoseconds
|
||||||
|
// TODO This probably breaks at edge cases like MIN/MAX
|
||||||
|
RealmInstant.from(sec + 1, -1_000_000 + nano)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package net.buzzert.kordophone.backend.db.model
|
||||||
|
|
||||||
|
import android.view.Display.Mode
|
||||||
|
import io.realm.kotlin.Realm
|
||||||
|
import io.realm.kotlin.types.RealmInstant
|
||||||
|
import io.realm.kotlin.types.RealmObject
|
||||||
|
import io.realm.kotlin.types.annotations.PrimaryKey
|
||||||
|
import net.buzzert.kordophone.backend.model.GUID
|
||||||
|
import org.mongodb.kbson.ObjectId
|
||||||
|
import net.buzzert.kordophone.backend.model.Message as ModelMessage
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
open class Message (
|
||||||
|
@PrimaryKey
|
||||||
|
var _id: String,
|
||||||
|
|
||||||
|
var text: String,
|
||||||
|
var guid: GUID,
|
||||||
|
var sender: String?,
|
||||||
|
var date: RealmInstant,
|
||||||
|
): RealmObject
|
||||||
|
{
|
||||||
|
constructor(): this(
|
||||||
|
_id = ObjectId().toString(),
|
||||||
|
text = "",
|
||||||
|
guid = "",
|
||||||
|
sender = null,
|
||||||
|
date = RealmInstant.now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toMessage(): ModelMessage {
|
||||||
|
return ModelMessage(
|
||||||
|
text = text,
|
||||||
|
guid = guid,
|
||||||
|
sender = sender,
|
||||||
|
date = Date.from(date.toInstant()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ModelMessage.toDatabaseMessage(): Message {
|
||||||
|
val from = this
|
||||||
|
return Message().apply {
|
||||||
|
text = from.text
|
||||||
|
guid = from.guid
|
||||||
|
sender = from.sender
|
||||||
|
date = from.date.toInstant().toRealmInstant()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ data class Conversation(
|
|||||||
val date: Date,
|
val date: Date,
|
||||||
|
|
||||||
@SerializedName("participantDisplayNames")
|
@SerializedName("participantDisplayNames")
|
||||||
val participants: List<String>?,
|
val participants: List<String>,
|
||||||
|
|
||||||
@SerializedName("displayName")
|
@SerializedName("displayName")
|
||||||
val displayName: String?,
|
val displayName: String?,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package net.buzzert.kordophone.backend
|
||||||
|
|
||||||
|
import net.buzzert.kordophone.backend.db.CachedChatDatabase
|
||||||
|
import net.buzzert.kordophone.backend.model.Conversation
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
class DatabaseTests {
|
||||||
|
@Test
|
||||||
|
fun testCreateRetrieve() {
|
||||||
|
val db = CachedChatDatabase.testDatabase()
|
||||||
|
|
||||||
|
val conversation = Conversation(
|
||||||
|
date = Date(),
|
||||||
|
participants = listOf("james@magahern.com"),
|
||||||
|
displayName = "Test",
|
||||||
|
unreadCount = 1,
|
||||||
|
lastMessagePreview = "Hello!",
|
||||||
|
guid = "1234",
|
||||||
|
)
|
||||||
|
|
||||||
|
db.writeConversations(listOf(conversation))
|
||||||
|
|
||||||
|
val readBackConversations = db.fetchConversations()
|
||||||
|
assertEquals(readBackConversations.count(), 1)
|
||||||
|
|
||||||
|
val readConversation = readBackConversations[0]
|
||||||
|
assertEquals(readConversation, conversation)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,4 +8,5 @@ plugins {
|
|||||||
id 'com.android.application' version '8.0.2' apply false
|
id 'com.android.application' version '8.0.2' apply false
|
||||||
id 'com.android.library' version '8.0.2' apply false
|
id 'com.android.library' version '8.0.2' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
||||||
|
id 'io.realm.kotlin' version '1.10.0' apply false
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user