Access Room DB inside Transformation.map android

253 Views Asked by At

In my Database i have a table called Account which looks kinda like this

@Entity(tableName = "accounts", primaryKeys = ["server_id", "account_id"])
data class Account(

    @ColumnInfo(name = "server_id")
    val serverId: Long,

    @ColumnInfo(name = "account_id")
    val accountId: Int,

    @ColumnInfo(name = "first_name", defaultValue = "")
    var firstname: String
)

So lets say that we have the following Database snapshot

server_id account_id first_name 1 10 Zak 1 11 Tom 1 12 Bob 1 13 Jim 1 14 Mike

Now i also have the following POJO which represents an available video room inside a chatRoom

data class RoomInfo(
    @SerializedName("m")
    val participantIntList: List<Int>,
    @SerializedName("o")
    val roomId: String,
    @SerializedName("s")
    val status: Int
)

So i get an incoming response from my Socket which is like the following

[
   {"m": [10, 11, 12], "o": "room_technical", "s": 1}, 
   {"m": [13, 14], "o": "room_operation", "s": 1}
]

which i map it in a List so i have

val roomInfo: LiveData<List<RoomInfo>> = socketManager.roomInfo

// So the value is basically the json converted to a list of RoomInfos using Gson

In order to display this available list of Rooms to the User i need to convert the m (which is the members that are inside the room right now) from accountIds to account.firstnames.

So what i want to have finally is a List of a new object called RoomInfoItem which will hold the list of the rooms with the accountIds converted to firstNames from the Account table of the Database.

data class RoomInfoItem(
    val roomInfo: RoomInfo,
    val participantNames: List<String>
)

So if we make the transformation we need to have the following result

RoomInfo (
      // RoomInfo 
      {"m": [10, 11, 12], "o": "room_technical", "s": 1}, 
      // Participant names
      ["Zak", "Tom", "Bob"]
   )

   RoomInfo (
      // RoomInfo 
       {"m": [13, 14], "o": "room_operation", "s": 1}, 
      // Participant names
      ["Jim", "Mike"]
   )

My Activity needs to observe a LiveData with the RoomInfoItems so what i want is given the LiveData<List> to transform it to LiveData<List>. How can i do that?

2

There are 2 best solutions below

1
On

Well, finally i could not find a solution but i think that what i am trying to achieve, cannot be done using the Transformation.switchMap or Transformation.map

2
On

As I understand you want get LiveData<List<RoomInfoItem>> by analogy LiveData<List<ResultData>> in my sample. And you have next condition: you want to observe list of RoomInfo and for each RoomInfo in this list you want to observe participantNames. (Each pair of RoomInfo and participantNames you map to RoomInfoItem). I think you can achive this behaviour by using MediatorLiveData. I show sample how you can do this bellow:

// For example we have method which returns liveData of List<String> - analogy to your List<RoomInfo>
fun firstLifeData(): LiveData<List<String>> {
    //TODO
}

// and we have method which returns liveData of List<Int> - analogy to your participantNames(List<String>)
fun secondLifeData(param: String): LiveData<List<Int>> {
    //TODO
}

//and analogy of your RoomInfoItem
data class ResultData(
    val param: String,
    val additionalData: List<Int>
)

Then I will show my idea of implementation of combined liveDatas:

@MainThread
fun <T> combinedLiveData(liveDatas: List<LiveData<T>>): LiveData<List<T>> {
    val mediatorLiveData = MediatorLiveData<List<T>>()
    // cache for values which emit each liveData, where key is an index of liveData  from input [liveDatas] list
    val liveDataIndexToValue: MutableMap<Int, T> = HashMap()
    // when [countOfNotEmittedLifeDatas] is 0 then each liveData from [liveDatas] emited value
    var countOfNotEmittedLifeDatas = liveDatas.size
    liveDatas.forEachIndexed { index, liveData ->
        mediatorLiveData.addSource(liveData) { value ->
            // when liveData emits first value then mack it by decrementing of countOfNotEmittedLifeDatas
            if (!liveDataIndexToValue.containsKey(index)) {
                countOfNotEmittedLifeDatas--
            }
            liveDataIndexToValue[index] = value
            // when countOfNotEmittedLifeDatas is 0 then all liveDatas emits at least one value
            if (countOfNotEmittedLifeDatas == 0) {
                // then we can push list of values next to client-side observer
                mediatorLiveData.value = liveDataIndexToValue.toListWithoutSavingOrder()
            }
        }
    }
    return mediatorLiveData
}

fun <V> Map<Int, V>.toListWithoutSavingOrder(): List<V> = this.values.toList()

/**
 *  Key should be an order
 */
fun <V> Map<Int, V>.toListWithSavingOrder(): List<V> = this.entries.sortedBy { it.key }.map { it.value }
/*
or you can run [for] cycle by liveDataIndexToValue in [combinedLiveData] method or apply [mapIndexed] like:

    liveDatas.mapIndexed{ index, _ ->
        liveDataIndexToValue[index]
    }
to receive ordered list.
 */


And how to use all of that together:

fun resultSample(): LiveData<List<ResultData>> {
    return firstLifeData().switchMap { listOfParams ->
        val liveDatas = listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }
        combinedLiveData(liveDatas)
    }
}

// u can add extension function like:
fun <T> List<LiveData<T>>.combined(): LiveData<List<T>> = combinedLiveData(this)

// and then use it in this way
fun resultSample_2(): LiveData<List<ResultData>> = firstLifeData().switchMap { listOfParams ->
    listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }.combined()
}

I suggest you to consider using room's Relations. I think by room's Relations you can get LiveData<RoomInfoItem> . I cant get you more details about this approach because I don't know details about your data scheme and domain, at the moment.