Private
Public Access
1
0

Started working on Cache Database

This commit is contained in:
2023-08-09 01:32:38 -07:00
parent fde5bb9f53
commit 4724ae5728
8 changed files with 232 additions and 1 deletions

View File

@@ -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'

View File

@@ -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() }
}
}

View File

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

View File

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

View File

@@ -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()
}
}

View File

@@ -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?,

View File

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

View File

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