yayan 1 year ago
parent
commit
018797ede3

+ 29 - 9
app/src/main/java/io/nexilis/alpha/ui/main/Chat.kt

@@ -1,7 +1,9 @@
 package io.nexilis.alpha.ui.main
 
 import android.text.format.DateUtils
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
+import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.rememberLazyListState
@@ -10,7 +12,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.Send
+import androidx.compose.material.icons.filled.AccessTime
 import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Done
 import androidx.compose.material.icons.filled.DoneAll
 import androidx.compose.material3.*
 import androidx.compose.runtime.*
@@ -29,9 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
 import androidx.navigation.NavHostController
 import io.nexilis.alpha.ui.viewmodel.MainViewModel
 import io.nexilis.service.data.entities.Message
+import io.nexilis.service.data.entities.MessageStatus
 import io.nexilis.service.data.viewmodels.BuddyViewModel
+import io.nexilis.service.data.viewmodels.MessageStatusViewModel
 import io.nexilis.service.data.viewmodels.MessageViewModel
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewModel) {
     var textInput by rememberSaveable(stateSaver = TextFieldValue.Saver) {
@@ -42,6 +49,7 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
     val buddy by buddyViewModel.getBuddy(pin).observeAsState()
     val messageModel: MessageViewModel = hiltViewModel()
     val list by messageModel.getOpposite(pin).observeAsState()
+    val messageStatusModel: MessageStatusViewModel = hiltViewModel()
     LaunchedEffect(buddy) {
         buddy?.first_name?.let {
             mainViewModel.setTitle("$it ${buddy?.last_name ?: ""}".trim())
@@ -83,6 +91,7 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
                                             shape = RoundedCornerShape(16.dp)
                                             clip = true
                                         }
+                                        .combinedClickable(onLongClick = {}, onClick = {})
                                         .background(color = MaterialTheme.colorScheme.primaryContainer)
                                         .padding(8.dp),
                                     text = list?.get(it)?.message_text ?: "",
@@ -103,14 +112,24 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
                                         style = MaterialTheme.typography.bodySmall,
                                     )
                                     Spacer(modifier = Modifier.width(6.dp))
-                                    Icon(
-                                        imageVector = Icons.Default.DoneAll,
-                                        contentDescription = "",
-                                        modifier = Modifier
-                                            .align(Alignment.Bottom)
-                                            .size(16.dp),
-                                        tint = Color(0xFF0075F1)
-                                    )
+                                    message?.message_id?.let {
+                                        val messageStatus by messageStatusModel.get(it, pin)
+                                            .observeAsState()
+                                        Icon(
+                                            imageVector = when (messageStatus?.status) {
+                                                1, 2 -> Icons.Default.Done
+                                                3 -> Icons.Default.DoneAll
+                                                4 -> Icons.Default.DoneAll
+                                                else -> Icons.Default.AccessTime
+                                            },
+                                            contentDescription = "",
+                                            modifier = Modifier
+                                                .align(Alignment.Bottom)
+                                                .size(16.dp),
+                                            tint = if (messageStatus?.status == 4) Color(0xFF0075F1) else Color.Gray
+                                        )
+                                    }
+
                                 }
                             }
                         } else {
@@ -126,6 +145,7 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
                                             shape = RoundedCornerShape(16.dp)
                                             clip = true
                                         }
+                                        .combinedClickable(onLongClick = {}, onClick = {})
                                         .background(color = MaterialTheme.colorScheme.surface)
                                         .padding(8.dp),
                                     text = list?.get(it)?.message_text ?: "",
@@ -134,7 +154,7 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
                                 Row(
                                     modifier = Modifier
                                         .padding(3.dp)
-                                        .align(Alignment.Start)
+                                        .align(Alignment.End)
                                 ) {
                                     Text(
                                         text = DateUtils.formatDateTime(

+ 49 - 3
cpaas-lite/src/main/java/io/nexilis/service/core/Incoming.kt

@@ -9,6 +9,7 @@ import io.nexilis.service.data.entities.Buddy
 import io.nexilis.service.data.entities.Group
 import io.nexilis.service.data.entities.GroupMember
 import io.nexilis.service.data.entities.Message
+import io.nexilis.service.data.entities.MessageStatus
 import io.nexilis.service.data.rooms.ApiRoomDatabase
 import io.nexilis.service.pass
 import io.nexilis.service.tag
@@ -72,9 +73,7 @@ class Incoming private constructor() {
                 }
 
                 "A002" -> {
-                    context.getSharedPreferences().getString("pin", "").let {
-                        Service.sendAck(it, data.status)
-                    }
+                    statusMessage(data)
                 }
 
                 else -> {
@@ -456,4 +455,51 @@ class Incoming private constructor() {
         }
     }
 
+    private suspend fun statusMessage(data: Data) {
+        try {
+            val me = context.getSharedPreferences().getString("pin", "")
+            val messageIds = data.bodies["A18"] ?: ""
+            if (messageIds.isEmpty()) return
+            val status = data.bodies["A15"]?.toInt() ?: 0
+            if (status == 0) return
+            val opposite = data.bodies["A01"] ?: ""
+            if (opposite.isEmpty()) return
+            val time = data.bodies["A19"]?.toLong() ?: System.currentTimeMillis()
+            messageIds.split(",").forEach {
+                val messageId = it.trim()
+                when (status) {
+                    3 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateDelivered(
+                        status = status,
+                        timeDelivered = time,
+                        messageId = messageId,
+                        pin = opposite
+                    )
+
+                    4 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateRead(
+                        status = status,
+                        timeRead = time,
+                        messageId = messageId,
+                        pin = opposite
+                    )
+
+                    8 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateAck(
+                        status = status,
+                        timeAck = time,
+                        messageId = messageId,
+                        pin = opposite
+                    )
+
+                    else -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateStatus(
+                        status = status,
+                        messageId = messageId,
+                        pin = opposite
+                    )
+                }
+            }
+            Service.sendAck(me, data.status)
+        } catch (e: Exception) {
+            Log.e(tag, e.message, e)
+        }
+    }
+
 }

+ 14 - 0
cpaas-lite/src/main/java/io/nexilis/service/data/daos/MessageDao.kt

@@ -25,12 +25,26 @@ abstract class MessageDao {
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     abstract suspend fun _insertSummary(entity: MessageSummary)
 
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    abstract suspend fun _insertStatus(entity: MessageStatus)
+
     @Query("delete from Message")
     abstract suspend fun deleteAll()
 
     suspend fun insert(entity: Message) {
         _insert(entity)
         _insertSummary(MessageSummary(entity.opposite_pin, entity.message_id))
+        _insertStatus(
+            MessageStatus(
+                message_id = entity.message_id,
+                f_pin = entity.opposite_pin,
+                user_id = entity.opposite_pin,
+                last_update = System.currentTimeMillis(),
+                time_delivered = 0,
+                time_read = 0,
+                time_ack = 0,
+            )
+        )
     }
 
 }

+ 38 - 0
cpaas-lite/src/main/java/io/nexilis/service/data/daos/MessageStatusDao.kt

@@ -16,9 +16,47 @@ interface MessageStatusDao {
     @Query("select * from MessageStatus where message_id = :id")
     fun get(id: String): LiveData<List<MessageStatus>>
 
+    @Query("select * from MessageStatus where message_id = :id and f_pin = :pin")
+    fun get(id: String, pin: String): LiveData<MessageStatus>
+
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     suspend fun insert(entity: MessageStatus)
 
+    @Query("update MessageStatus set status = :status, last_update = :lastUpdate where message_id = :messageId and f_pin = :pin")
+    suspend fun updateStatus(
+        status: Int,
+        lastUpdate: Long = System.currentTimeMillis(),
+        messageId: String,
+        pin: String
+    )
+
+    @Query("update MessageStatus set status = :status, last_update = :lastUpdate, time_delivered = :timeDelivered where message_id = :messageId and f_pin = :pin")
+    suspend fun updateDelivered(
+        status: Int,
+        lastUpdate: Long = System.currentTimeMillis(),
+        timeDelivered: Long,
+        messageId: String,
+        pin: String
+    )
+
+    @Query("update MessageStatus set status = :status, last_update = :lastUpdate, time_read = :timeRead where message_id = :messageId and f_pin = :pin")
+    suspend fun updateRead(
+        status: Int,
+        lastUpdate: Long = System.currentTimeMillis(),
+        timeRead: Long,
+        messageId: String,
+        pin: String
+    )
+
+    @Query("update MessageStatus set status = :status, last_update = :lastUpdate, time_ack = :timeAck where message_id = :messageId and f_pin = :pin")
+    suspend fun updateAck(
+        status: Int,
+        lastUpdate: Long = System.currentTimeMillis(),
+        timeAck: Long,
+        messageId: String,
+        pin: String
+    )
+
     @Query("delete from MessageStatus")
     suspend fun deleteAll()
 

+ 30 - 9
cpaas-lite/src/main/java/io/nexilis/service/data/repositories/MessageRepository.kt

@@ -5,19 +5,24 @@ import io.nexilis.service.Service
 import io.nexilis.service.core.Data
 import io.nexilis.service.core.toStupidString
 import io.nexilis.service.data.daos.MessageDao
+import io.nexilis.service.data.daos.MessageStatusDao
 import io.nexilis.service.data.entities.MainEntity
 import io.nexilis.service.data.entities.Message
+import io.nexilis.service.data.entities.MessageStatus
 import javax.inject.Inject
 
-class MessageRepository @Inject constructor(private val dao: MessageDao) : Repository {
+class MessageRepository @Inject constructor(
+    private val dao: MessageDao,
+    private val daoStatus: MessageStatusDao
+) : Repository {
 
     val all: LiveData<List<Message>> = dao.getAll()
 
-    fun get(id: String) : LiveData<Message> {
+    fun get(id: String): LiveData<Message> {
         return dao.get(id)
     }
 
-    fun getOpposite(pin: String) : LiveData<List<Message>> {
+    fun getOpposite(pin: String): LiveData<List<Message>> {
         return dao.getOpposite(pin)
     }
 
@@ -77,11 +82,27 @@ class MessageRepository @Inject constructor(private val dao: MessageDao) : Repos
         if (message.broadcast_flag > 0) {
             bodies["B4"] = message.broadcast_flag.toString()
         }
-        Service.sendSync(Data(
-            code = "S0",
-            status = message.message_id,
-            f_pin = message.f_pin,
-            bodies = bodies
-        ))
+        Service.sendSync(
+            Data(
+                code = "S0",
+                status = message.message_id,
+                f_pin = message.f_pin,
+                bodies = bodies
+            )
+        )?.let {
+            daoStatus.insert(
+                MessageStatus(
+                    message_id = entity.message_id,
+                    status = it.bodies["A15"]?.toInt() ?: 2, // delivering
+                    f_pin = entity.opposite_pin,
+                    user_id = entity.opposite_pin,
+                    last_update = System.currentTimeMillis(),
+                    time_delivered = 0,
+                    time_read = 0,
+                    time_ack = 0,
+                )
+            )
+        }
+
     }
 }

+ 8 - 0
cpaas-lite/src/main/java/io/nexilis/service/data/repositories/MessageStatusRepository.kt

@@ -10,6 +10,14 @@ class MessageStatusRepository @Inject constructor(private val dao: MessageStatus
 
     val all: LiveData<List<MessageStatus>> = dao.getAll()
 
+    fun get(id: String) : LiveData<List<MessageStatus>> {
+        return dao.get(id)
+    }
+
+    fun get(id: String, pin: String) : LiveData<MessageStatus> {
+        return dao.get(id, pin)
+    }
+
     override suspend fun insert(entity: MainEntity) {
         dao.insert(entity as MessageStatus)
     }

+ 22 - 0
cpaas-lite/src/main/java/io/nexilis/service/data/viewmodels/MessageStatusViewModel.kt

@@ -0,0 +1,22 @@
+package io.nexilis.service.data.viewmodels
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.nexilis.service.data.entities.MessageStatus
+import io.nexilis.service.data.repositories.MessageStatusRepository
+import javax.inject.Inject
+
+@HiltViewModel
+class MessageStatusViewModel @Inject constructor(private val repository: MessageStatusRepository) :
+    ViewModel() {
+    val all: LiveData<List<MessageStatus>> = repository.all
+
+    fun get(id: String) : LiveData<List<MessageStatus>> {
+        return repository.get(id)
+    }
+
+    fun get(id: String, pin: String) : LiveData<MessageStatus> {
+        return repository.get(id, pin)
+    }
+}