-
-
Notifications
You must be signed in to change notification settings - Fork 56
/
ChatManager.kt
113 lines (96 loc) · 3.72 KB
/
ChatManager.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// SPDX-FileCopyrightText: 2019-2021 aTox contributors
//
// SPDX-License-Identifier: GPL-3.0-only
package ltd.evilcorp.domain.feature
import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.StandardCharsets
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import ltd.evilcorp.core.repository.ContactRepository
import ltd.evilcorp.core.repository.MessageRepository
import ltd.evilcorp.core.vo.ConnectionStatus
import ltd.evilcorp.core.vo.Message
import ltd.evilcorp.core.vo.MessageType
import ltd.evilcorp.core.vo.Sender
import ltd.evilcorp.domain.tox.MAX_MESSAGE_LENGTH
import ltd.evilcorp.domain.tox.PublicKey
import ltd.evilcorp.domain.tox.Tox
private fun String.chunked(chunkSizeInBytes: Int): MutableList<String> {
val encoder = StandardCharsets.UTF_8.newEncoder()
val tmp = ByteBuffer.allocate(chunkSizeInBytes - 1)
val input = CharBuffer.wrap(this)
val chunks: MutableList<String> = ArrayList()
var currentIdx = 0
do {
val res = encoder.encode(input, tmp, true)
val nextIdx = this.length - input.length
chunks.add(this.substring(currentIdx, nextIdx))
currentIdx = nextIdx
tmp.rewind()
} while (res.isOverflow)
return chunks
}
@Singleton
class ChatManager @Inject constructor(
private val scope: CoroutineScope,
private val contactRepository: ContactRepository,
private val messageRepository: MessageRepository,
private val tox: Tox,
) {
var activeChat = ""
set(value) {
field = value
if (value.isNotEmpty()) {
scope.launch {
contactRepository.setHasUnreadMessages(value, false)
}
}
}
fun messagesFor(publicKey: PublicKey) = messageRepository.get(publicKey.string())
fun sendMessage(publicKey: PublicKey, message: String, type: MessageType = MessageType.Normal) =
scope.launch {
if (contactRepository.get(publicKey.string()).first().connectionStatus == ConnectionStatus.None) {
queueMessage(publicKey, message, type)
return@launch
}
val msgs = message.chunked(MAX_MESSAGE_LENGTH)
while (msgs.size > 1) {
tox.sendMessage(publicKey, msgs.removeFirst(), type)
}
messageRepository.add(
Message(
publicKey.string(),
message,
Sender.Sent,
type,
tox.sendMessage(publicKey, msgs.first(), type),
),
)
}
private fun queueMessage(publicKey: PublicKey, message: String, type: MessageType) =
messageRepository.add(Message(publicKey.string(), message, Sender.Sent, type, Int.MIN_VALUE))
fun resend(messages: List<Message>) = scope.launch {
for (message in messages) {
val msgs = message.message.chunked(MAX_MESSAGE_LENGTH)
while (msgs.size > 1) {
tox.sendMessage(PublicKey(message.publicKey), msgs.removeFirst(), message.type)
}
messageRepository.setCorrelationId(
message.id,
tox.sendMessage(PublicKey(message.publicKey), msgs.first(), message.type),
)
}
}
fun deleteMessage(id: Long) = scope.launch {
messageRepository.deleteMessage(id)
}
fun clearHistory(publicKey: PublicKey) = scope.launch {
messageRepository.delete(publicKey.string())
contactRepository.setLastMessage(publicKey.string(), 0)
}
fun setTyping(publicKey: PublicKey, typing: Boolean) = tox.setTyping(publicKey, typing)
}