session-android/src/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt

145 lines
5.5 KiB
Kotlin
Raw Normal View History

2020-05-12 03:46:11 +02:00
package org.thoughtcrime.securesms.loki.api
import android.content.Context
import android.database.ContentObserver
2020-09-25 13:11:55 +02:00
import android.graphics.Bitmap
2019-10-11 03:37:45 +02:00
import android.text.TextUtils
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseContentProviders
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
2020-09-25 13:11:55 +02:00
import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
2019-10-11 03:37:45 +02:00
import org.thoughtcrime.securesms.util.Util
2020-10-19 06:12:06 +02:00
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatInfo
2020-07-15 04:24:43 +02:00
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
2020-07-15 06:26:20 +02:00
class PublicChatManager(private val context: Context) {
2020-07-15 04:24:43 +02:00
private var chats = mutableMapOf<Long, PublicChat>()
2020-07-15 06:26:20 +02:00
private val pollers = mutableMapOf<Long, PublicChatPoller>()
private val observers = mutableMapOf<Long, ContentObserver>()
private var isPolling = false
2020-07-08 03:30:00 +02:00
public fun areAllCaughtUp():Boolean {
var areAllCaughtUp = true
refreshChatsAndPollers()
2020-07-08 03:30:00 +02:00
for ((threadID, chat) in chats) {
2020-10-26 23:42:39 +01:00
val poller = pollers[threadID]
2020-10-26 23:59:29 +01:00
areAllCaughtUp = if (poller != null) areAllCaughtUp && poller.isCaughtUp else true
}
2020-07-08 03:30:00 +02:00
return areAllCaughtUp
}
2020-07-08 03:30:00 +02:00
public fun markAllAsNotCaughtUp() {
refreshChatsAndPollers()
2020-07-08 03:30:00 +02:00
for ((threadID, chat) in chats) {
2020-07-15 06:26:20 +02:00
val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
2020-07-08 03:30:00 +02:00
poller.isCaughtUp = false
}
}
public fun startPollersIfNeeded() {
refreshChatsAndPollers()
for ((threadId, chat) in chats) {
2020-07-15 06:26:20 +02:00
val poller = pollers[threadId] ?: PublicChatPoller(context, chat)
poller.startIfNeeded()
listenToThreadDeletion(threadId)
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
}
isPolling = true
}
public fun stopPollers() {
pollers.values.forEach { it.stop() }
isPolling = false
}
2020-07-15 04:24:43 +02:00
public fun addChat(server: String, channel: Long): Promise<PublicChat, Exception> {
2020-09-25 13:11:55 +02:00
val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI
?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
return groupChatAPI.getAuthToken(server).bind {
groupChatAPI.getChannelInfo(channel, server)
}.map {
addChat(server, channel, it)
}
}
2020-10-19 06:12:06 +02:00
public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat {
2020-09-25 13:11:55 +02:00
val chat = PublicChat(channel, server, info.displayName, true)
2020-05-12 08:33:04 +02:00
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
2020-10-19 06:12:06 +02:00
var profilePicture: Bitmap? = null
// Create the group if we don't have one
if (threadID < 0) {
2020-10-19 06:12:06 +02:00
if (info.profilePictureURL.isNotEmpty()) {
2020-11-12 03:02:38 +01:00
val profilePictureAsByteArray = ApplicationContext.getInstance(context).publicChatAPI
?.downloadOpenGroupProfilePicture(server, info.profilePictureURL)
profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
2020-09-25 13:11:55 +02:00
}
2020-10-19 06:12:06 +02:00
val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, chat.displayName)
threadID = result.threadId
}
2019-10-15 04:39:17 +02:00
DatabaseFactory.getLokiThreadDatabase(context).setPublicChat(chat, threadID)
// Set our name on the server
2019-10-11 03:37:45 +02:00
val displayName = TextSecurePreferences.getProfileName(context)
if (!TextUtils.isEmpty(displayName)) {
2020-07-15 04:24:43 +02:00
ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server)
2019-10-11 03:37:45 +02:00
}
// Start polling
Util.runOnMain{ startPollersIfNeeded() }
return chat
}
private fun refreshChatsAndPollers() {
2019-10-15 04:39:17 +02:00
val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats()
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
// Only append to chats if we have a thread for the chat
2020-05-12 08:33:04 +02:00
chats = chatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap()
}
private fun listenToThreadDeletion(threadID: Long) {
if (threadID < 0 || observers[threadID] != null) { return }
val observer = createDeletionObserver(threadID, Runnable {
val chat = chats[threadID]
// Reset last message cache
if (chat != null) {
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(context)
apiDatabase.removeLastDeletionServerID(chat.channel, chat.server)
apiDatabase.removeLastMessageServerID(chat.channel, chat.server)
}
2019-10-15 04:39:17 +02:00
DatabaseFactory.getLokiThreadDatabase(context).removePublicChat(threadID)
pollers.remove(threadID)?.stop()
observers.remove(threadID)
startPollersIfNeeded()
})
observers[threadID] = observer
context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer)
}
private fun createDeletionObserver(threadID: Long, onDelete: Runnable): ContentObserver {
return object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
// Stop the poller if thread is deleted
try {
if (!DatabaseFactory.getThreadDatabase(context).hasThread(threadID)) {
onDelete.run()
context.applicationContext.contentResolver.unregisterContentObserver(this)
}
} catch (e: Exception) {
// TODO: Handle
}
}
}
}
}