session-android/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt

135 lines
5.8 KiB
Kotlin

package org.thoughtcrime.securesms.groups
import android.content.Context
import android.graphics.Bitmap
import androidx.annotation.WorkerThread
import okhttp3.HttpUrl
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.messaging.open_groups.OpenGroupV2
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV2
import org.session.libsession.utilities.Util
import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.BitmapUtil
import java.util.concurrent.Executors
object OpenGroupManager {
private val executorService = Executors.newScheduledThreadPool(4)
private var pollers = mutableMapOf<String, OpenGroupPollerV2>() // One for each server
private var isPolling = false
val isAllCaughtUp: Boolean
get() {
pollers.values.forEach { poller ->
val jobID = poller.secondToLastJob?.id
jobID?.let {
val storage = MessagingModuleConfiguration.shared.storage
if (storage.getMessageReceiveJob(jobID) == null) {
// If the second to last job is done, it means we are now handling the last job
poller.isCaughtUp = true
poller.secondToLastJob = null
}
}
if (!poller.isCaughtUp) { return false }
}
return true
}
fun startPolling() {
if (isPolling) { return }
isPolling = true
val storage = MessagingModuleConfiguration.shared.storage
val servers = storage.getAllV2OpenGroups().values.map { it.server }.toSet()
servers.forEach { server ->
pollers[server]?.stop() // Shouldn't be necessary
val poller = OpenGroupPollerV2(server, executorService)
poller.startIfNeeded()
pollers[server] = poller
}
}
fun stopPolling() {
pollers.forEach { it.value.stop() }
pollers.clear()
}
@WorkerThread
fun add(server: String, room: String, publicKey: String, context: Context) {
val openGroupID = "$server.$room"
var threadID = GroupManager.getOpenGroupThreadID(openGroupID, context)
val storage = MessagingModuleConfiguration.shared.storage
val threadDB = DatabaseComponent.get(context).lokiThreadDatabase()
// Check it it's added already
val existingOpenGroup = threadDB.getOpenGroupChat(threadID)
if (existingOpenGroup != null) { return }
// Clear any existing data if needed
storage.removeLastDeletionServerID(room, server)
storage.removeLastMessageServerID(room, server)
// Store the public key
storage.setOpenGroupPublicKey(server,publicKey)
// Get an auth token
OpenGroupAPIV2.getAuthToken(room, server).get()
// Get group info
val info = OpenGroupAPIV2.getInfo(room, server).get()
// Download the group image
// FIXME: Don't wait for the image to download
val image: Bitmap?
if (threadID < 0) {
val profilePictureAsByteArray = try {
OpenGroupAPIV2.downloadOpenGroupProfilePicture(info.id, server).get()
} catch (e: Exception) {
null
}
image = BitmapUtil.fromByteArray(profilePictureAsByteArray)
// Create the group locally
threadID = GroupManager.createOpenGroup(openGroupID, context, image, info.name).threadId
}
val openGroup = OpenGroupV2(server, room, info.name, publicKey)
threadDB.setOpenGroupChat(openGroup, threadID)
// Start the poller if needed
pollers[server]?.startIfNeeded() ?: run {
val poller = OpenGroupPollerV2(server, executorService)
Util.runOnMain { poller.startIfNeeded() }
pollers[server] = poller
}
}
fun delete(server: String, room: String, context: Context) {
val storage = MessagingModuleConfiguration.shared.storage
val threadDB = DatabaseComponent.get(context).threadDatabase()
val openGroupID = "$server.$room"
val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context)
val recipient = threadDB.getRecipientForThreadId(threadID) ?: return
val groupID = recipient.address.serialize()
// Stop the poller if needed
val openGroups = storage.getAllV2OpenGroups().filter { it.value.server == server }
if (openGroups.count() == 1) {
val poller = pollers[server]
poller?.stop()
pollers.remove(server)
}
// Delete
storage.removeLastDeletionServerID(room, server)
storage.removeLastMessageServerID(room, server)
val lokiThreadDB = DatabaseComponent.get(context).lokiThreadDatabase()
lokiThreadDB.removeOpenGroupChat(threadID)
ThreadUtils.queue {
threadDB.deleteConversation(threadID) // Must be invoked on a background thread
GroupManager.deleteGroup(groupID, context) // Must be invoked on a background thread
}
}
fun addOpenGroup(urlAsString: String, context: Context) {
val url = HttpUrl.parse(urlAsString) ?: return
val builder = HttpUrl.Builder().scheme(url.scheme()).host(url.host())
if (url.port() != 80 || url.port() != 443) {
// Non-standard port; add to server
builder.port(url.port())
}
val server = builder.build()
val room = url.pathSegments().firstOrNull() ?: return
val publicKey = url.queryParameter("public_key") ?: return
add(server.toString().removeSuffix("/"), room, publicKey, context)
}
}