Coverage Summary for Class: MessageMigrationUtils (com.greybox.projectmesh.messaging.utils)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
| MessageMigrationUtils |
100%
(1/1)
|
66.7%
(2/3)
|
100%
(4/4)
|
90%
(9/10)
|
//script to help migrate existing messages
package com.greybox.projectmesh.messaging.utils
import android.content.Context
import android.util.Log
import com.greybox.projectmesh.GlobalApp
import com.greybox.projectmesh.db.MeshDatabase
import com.greybox.projectmesh.testing.TestDeviceService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.instance
/**
* Utility responsible for migrating legacy messages to the newer
* conversation-ID–based chat naming format.
*
* It inspects all existing messages, infers correct conversation IDs,
* and rewrites their `chat` field when necessary.
*
* This allows older installations to transition cleanly to the
* standardized conversation model.
*/
class MessageMigrationUtils(
override val di: DI
): DIAware {
private val db: MeshDatabase by di.instance()
/**
* Migrates all historical messages so that each message's `chat`
* value follows the modern conversation ID format.
*
* Steps performed:
* - Loads all messages
* - Groups them by legacy chat name
* - Determines correct UUID association for each chat group
* - Generates a conversation ID using local + remote UUIDs
* - Rewrites messages with updated chat names
*
* Errors are logged but do not stop the migration process.
*/
suspend fun migrateMessagesToChatIds() {
withContext(Dispatchers.IO){
try {
//get all messages by id
val messages = db.messageDao().getAll()
Log.d("MessageMigration", "Found ${messages.size} messages to check for migration")
//group messages by their current chat names
val messagesByChat = messages.groupBy { it.chat }
//process each chat group
messagesByChat.forEach { (chatName, messagesInChat) ->
//skip messages that already appear by using convo id format
if (chatName.contains("-") && (chatName.count { it == '-' } >= 1)){
Log.d("MessageMigration", "Chat $chatName already appears to use conversation ID format")
return@forEach
}
//determine the Uuid for this chat
val userUuid = when (chatName) {
TestDeviceService.TEST_DEVICE_NAME -> "test-device-uuid"
TestDeviceService.TEST_DEVICE_NAME_OFFLINE -> "offline-test-device-uuid"
else -> {
try {
//try to find users by checking connected users first
val allUsers = GlobalApp.GlobalUserRepo.userRepository.getAllUsers()
val matchingUser = allUsers.find { user -> user.name == chatName }
if (matchingUser != null) {
matchingUser.uuid
} else {
// If not found in connected users, create a placeholder UUID
"unknown-${chatName}"
}
}catch (e: Exception) {
Log.e(
"MessageMigration",
"Error finding user for chat $chatName",
e
)
"unknown-${chatName}"
}
}
}
//create the new conversation ID
val localUuid = GlobalApp.GlobalUserRepo.prefs.getString("UUID", null) ?: "local-user"
val newChatName = createConversationId(localUuid, userUuid)
if (chatName != newChatName) {
Log.d("MessageMigration", "Migrating ${messagesInChat.size} messages from '$chatName' to '$newChatName'")
//Create new messages with the updated chat name
val updatedMessages = messagesInChat.map {
it.copy (chat = newChatName)
}
//Delete old messages and insert updated ones
db.messageDao().deleteAll(messagesInChat)
for (message in updatedMessages) {
db.messageDao().addMessage(message)
}
Log.d("MessageMigration", "Successfully migrated messages for chat 'chatName'")
}
}
}catch (e:Exception){
Log.e("MessageMigration", "Error during message migration", e)
}
}
}
/**
* Generates a consistent conversation ID using two UUIDs.
*
* Special cases:
* - Test device UUIDs map to fixed, readable conversation IDs.
*
* @param uuid1 Local UUID.
* @param uuid2 Remote UUID.
* @return A stable, sorted, hyphen-joined conversation ID.
*/
internal fun createConversationId(uuid1: String, uuid2: String): String {
// Special cases for test devices
if (uuid2 == "test-device-uuid") {
return "local-user-test-device-uuid"
}
if (uuid2 == "offline-test-device-uuid") {
return "local-user-offline-test-device-uuid"
}
return listOf(uuid1, uuid2).sorted().joinToString("-")
}
}