sync update to libsession & clean

This commit is contained in:
Ryan ZHAO 2021-01-27 10:54:25 +11:00
parent 1f1ffdafdd
commit 9eacdd7b3e
12 changed files with 171 additions and 120 deletions

View File

@ -12,7 +12,7 @@ import android.util.Pair;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.session.libsession.utilities.ThreadUtil;
import org.session.libsession.utilities.ThreadUtils;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.concurrent.ListenableFuture;
import org.session.libsession.utilities.concurrent.SettableFuture;
@ -25,7 +25,7 @@ public class AudioRecorder {
private static final String TAG = AudioRecorder.class.getSimpleName();
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
private static final ExecutorService executor = ThreadUtils.newDynamicSingleThreadedExecutor();
private final Context context;

View File

@ -1,7 +1,6 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
@ -48,8 +47,6 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
// annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
// kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
implementation 'com.annimon:stream:1.1.8'
implementation 'com.makeramen:roundedimageview:2.1.0'

View File

@ -2,14 +2,14 @@ package org.session.libsession.messaging.opengroups
import org.session.libsignal.service.internal.util.JsonUtil
public data class OpenGroup(
public val channel: Long,
data class OpenGroup(
val channel: Long,
private val serverURL: String,
public val displayName: String,
public val isDeletable: Boolean
val displayName: String,
val isDeletable: Boolean
) {
public val server get() = serverURL.toLowerCase()
public val id get() = getId(channel, server)
val server get() = serverURL.toLowerCase()
val id get() = getId(channel, server)
companion object {
@ -31,7 +31,7 @@ public data class OpenGroup(
}
}
public fun toJSON(): Map<String, Any> {
fun toJSON(): Map<String, Any> {
return mapOf( "channel" to channel, "server" to server, "displayName" to displayName, "isDeletable" to isDeletable )
}
}

View File

@ -9,14 +9,16 @@ import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.utilities.DotNetAPI
import org.session.libsession.messaging.fileserver.FileServerAPI
import org.session.libsession.utilities.ThreadUtils
import org.session.libsession.utilities.createContext
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.service.internal.util.Base64
import org.session.libsignal.service.internal.util.Hex
import org.session.libsignal.service.internal.util.JsonUtil
import org.session.libsignal.service.loki.api.LokiDotNetAPI
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
import org.session.libsignal.service.loki.utilities.DownloadUtilities
import org.session.libsignal.service.loki.utilities.createContext
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
import org.session.libsignal.service.loki.utilities.retryIfNeeded
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
@ -25,7 +27,7 @@ import java.util.*
object OpenGroupAPI: DotNetAPI() {
private val moderators: HashMap<String, HashMap<Long, Set<String>>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
val sharedContext = Kovenant.createContext("LokiPublicChatAPISharedContext")
val sharedContext = Kovenant.createContext()
// region Settings
private val fallbackBatchCount = 64
@ -36,15 +38,15 @@ object OpenGroupAPI: DotNetAPI() {
private val channelInfoType = "net.patter-app.settings"
private val attachmentType = "net.app.core.oembed"
@JvmStatic
public val openGroupMessageType = "network.loki.messenger.publicChat"
val openGroupMessageType = "network.loki.messenger.publicChat"
@JvmStatic
public val profilePictureType = "network.loki.messenger.avatar"
val profilePictureType = "network.loki.messenger.avatar"
fun getDefaultChats(): List<OpenGroup> {
return listOf() // Don't auto-join any open groups right now
}
public fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean {
fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean {
if (moderators[server] != null && moderators[server]!![channel] != null) {
return moderators[server]!![channel]!!.contains(hexEncodedPublicKey)
}
@ -53,7 +55,7 @@ object OpenGroupAPI: DotNetAPI() {
// endregion
// region Public API
public fun getMessages(channel: Long, server: String): Promise<List<OpenGroupMessage>, Exception> {
fun getMessages(channel: Long, server: String): Promise<List<OpenGroupMessage>, Exception> {
Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.")
val storage = MessagingConfiguration.shared.storage
val parameters = mutableMapOf<String, Any>( "include_annotations" to 1 )
@ -158,7 +160,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun getDeletedMessageServerIDs(channel: Long, server: String): Promise<List<Long>, Exception> {
fun getDeletedMessageServerIDs(channel: Long, server: String): Promise<List<Long>, Exception> {
Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.")
val storage = MessagingConfiguration.shared.storage
val parameters = mutableMapOf<String, Any>()
@ -190,12 +192,12 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun sendMessage(message: OpenGroupMessage, channel: Long, server: String): Promise<OpenGroupMessage, Exception> {
fun sendMessage(message: OpenGroupMessage, channel: Long, server: String): Promise<OpenGroupMessage, Exception> {
val deferred = deferred<OpenGroupMessage, Exception>()
val storage = MessagingConfiguration.shared.storage
val userKeyPair = storage.getUserKeyPair() ?: throw Error.Generic
val userDisplayName = storage.getUserDisplayName() ?: throw Error.Generic
Thread {
ThreadUtils.queue {
val signedMessage = message.sign(userKeyPair.second)
if (signedMessage == null) {
deferred.reject(Error.SigningFailed)
@ -225,11 +227,11 @@ object OpenGroupAPI: DotNetAPI() {
deferred.reject(it)
}
}
}.start()
}
return deferred.promise
}
public fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise<Long, Exception> {
fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise<Long, Exception> {
return retryIfNeeded(maxRetryCount) {
val isModerationRequest = !isSentByUser
Log.d("Loki", "Deleting message with ID: $messageServerID from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).")
@ -241,7 +243,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun deleteMessages(messageServerIDs: List<Long>, channel: Long, server: String, isSentByUser: Boolean): Promise<List<Long>, Exception> {
fun deleteMessages(messageServerIDs: List<Long>, channel: Long, server: String, isSentByUser: Boolean): Promise<List<Long>, Exception> {
return retryIfNeeded(maxRetryCount) {
val isModerationRequest = !isSentByUser
val parameters = mapOf( "ids" to messageServerIDs.joinToString(",") )
@ -254,7 +256,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun getModerators(channel: Long, server: String): Promise<Set<String>, Exception> {
fun getModerators(channel: Long, server: String): Promise<Set<String>, Exception> {
return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/get_moderators").then(sharedContext) { json ->
try {
@Suppress("UNCHECKED_CAST") val moderators = json["moderators"] as? List<String>
@ -272,7 +274,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun getChannelInfo(channel: Long, server: String): Promise<OpenGroupInfo, Exception> {
fun getChannelInfo(channel: Long, server: String): Promise<OpenGroupInfo, Exception> {
return retryIfNeeded(maxRetryCount) {
val parameters = mapOf( "include_annotations" to 1 )
execute(HTTPVerb.GET, server, "/channels/$channel", parameters = parameters).then(sharedContext) { json ->
@ -296,7 +298,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: OpenGroupInfo, isForcedUpdate: Boolean) {
fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: OpenGroupInfo, isForcedUpdate: Boolean) {
val storage = MessagingConfiguration.shared.storage
storage.setUserCount(channel, server, info.memberCount)
storage.updateTitle(groupID, info.displayName)
@ -309,7 +311,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? {
fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? {
val url = "${server.removeSuffix("/")}/${endpoint.removePrefix("/")}"
Log.d("Loki", "Downloading open group profile picture from \"$url\".")
val outputStream = ByteArrayOutputStream()
@ -325,7 +327,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun join(channel: Long, server: String): Promise<Unit, Exception> {
fun join(channel: Long, server: String): Promise<Unit, Exception> {
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.POST, server, "/channels/$channel/subscribe").then {
Log.d("Loki", "Joined channel with ID: $channel on server: $server.")
@ -333,7 +335,7 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun leave(channel: Long, server: String): Promise<Unit, Exception> {
fun leave(channel: Long, server: String): Promise<Unit, Exception> {
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.DELETE, server, "/channels/$channel/subscribe").then {
Log.d("Loki", "Left channel with ID: $channel on server: $server.")
@ -341,7 +343,15 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun getDisplayNames(publicKeys: Set<String>, server: String): Promise<Map<String, String>, Exception> {
fun ban(publicKey: String, server: String): Promise<Unit,Exception> {
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.POST, server, "/loki/v1/moderation/blacklist/@$publicKey").then {
Log.d("Loki", "Banned user with ID: $publicKey from $server")
}
}
}
fun getDisplayNames(publicKeys: Set<String>, server: String): Promise<Map<String, String>, Exception> {
return getUserProfiles(publicKeys, server, false).map(sharedContext) { json ->
val mapping = mutableMapOf<String, String>()
for (user in json) {
@ -355,17 +365,17 @@ object OpenGroupAPI: DotNetAPI() {
}
}
public fun setDisplayName(newDisplayName: String?, server: String): Promise<Unit, Exception> {
fun setDisplayName(newDisplayName: String?, server: String): Promise<Unit, Exception> {
Log.d("Loki", "Updating display name on server: $server.")
val parameters = mapOf( "name" to (newDisplayName ?: "") )
return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters).map { Unit }
}
public fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise<Unit, Exception> {
fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise<Unit, Exception> {
return setProfilePicture(server, Base64.encodeBytes(profileKey), url)
}
public fun setProfilePicture(server: String, profileKey: String, url: String?): Promise<Unit, Exception> {
fun setProfilePicture(server: String, profileKey: String, url: String?): Promise<Unit, Exception> {
Log.d("Loki", "Updating profile picture on server: $server.")
val value = when (url) {
null -> null

View File

@ -1,7 +1,7 @@
package org.session.libsession.messaging.opengroups
public data class OpenGroupInfo (
public val displayName: String,
public val profilePictureURL: String,
public val memberCount: Int
data class OpenGroupInfo (
val displayName: String,
val profilePictureURL: String,
val memberCount: Int
)

View File

@ -7,18 +7,18 @@ import org.session.libsignal.service.internal.util.Hex
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.whispersystems.curve25519.Curve25519
public data class OpenGroupMessage(
public val serverID: Long?,
public val senderPublicKey: String,
public val displayName: String,
public val body: String,
public val timestamp: Long,
public val type: String,
public val quote: Quote?,
public val attachments: MutableList<Attachment>,
public val profilePicture: ProfilePicture?,
public val signature: Signature?,
public val serverTimestamp: Long,
data class OpenGroupMessage(
val serverID: Long?,
val senderPublicKey: String,
val displayName: String,
val body: String,
val timestamp: Long,
val type: String,
val quote: Quote?,
val attachments: MutableList<Attachment>,
val profilePicture: ProfilePicture?,
val signature: Signature?,
val serverTimestamp: Long,
) {
// region Settings
@ -98,52 +98,52 @@ public data class OpenGroupMessage(
// endregion
// region Types
public data class ProfilePicture(
public val profileKey: ByteArray,
public val url: String,
data class ProfilePicture(
val profileKey: ByteArray,
val url: String,
)
public data class Quote(
public val quotedMessageTimestamp: Long,
public val quoteePublicKey: String,
public val quotedMessageBody: String,
public val quotedMessageServerID: Long? = null,
data class Quote(
val quotedMessageTimestamp: Long,
val quoteePublicKey: String,
val quotedMessageBody: String,
val quotedMessageServerID: Long? = null,
)
public data class Signature(
public val data: ByteArray,
public val version: Long,
data class Signature(
val data: ByteArray,
val version: Long,
)
public data class Attachment(
public val kind: Kind,
public val server: String,
public val serverID: Long,
public val contentType: String,
public val size: Int,
public val fileName: String,
public val flags: Int,
public val width: Int,
public val height: Int,
public val caption: String?,
public val url: String,
data class Attachment(
val kind: Kind,
val server: String,
val serverID: Long,
val contentType: String,
val size: Int,
val fileName: String,
val flags: Int,
val width: Int,
val height: Int,
val caption: String?,
val url: String,
/**
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
*/
public val linkPreviewURL: String?,
val linkPreviewURL: String?,
/**
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
*/
public val linkPreviewTitle: String?,
val linkPreviewTitle: String?,
) {
public val dotNetAPIType = when {
val dotNetAPIType = when {
contentType.startsWith("image") -> "photo"
contentType.startsWith("video") -> "video"
contentType.startsWith("audio") -> "audio"
else -> "other"
}
public enum class Kind(val rawValue: String) {
enum class Kind(val rawValue: String) {
Attachment("attachment"), LinkPreview("preview")
}
}

View File

@ -14,6 +14,7 @@ import org.session.libsignal.service.loki.api.*
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
import org.session.libsignal.service.loki.api.utilities.*
import org.session.libsession.utilities.AESGCM.EncryptionResult
import org.session.libsession.utilities.ThreadUtils
import org.session.libsession.utilities.getBodyForOnionRequest
import org.session.libsession.utilities.getHeadersForOnionRequest
import org.session.libsignal.service.loki.utilities.*
@ -83,12 +84,12 @@ object OnionRequestAPI {
*/
private fun testSnode(snode: Snode): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>()
Thread { // No need to block the shared context for this
ThreadUtils.queue { // No need to block the shared context for this
val url = "${snode.address}:${snode.port}/get_stats/v1"
try {
val json = HTTP.execute(HTTP.Verb.GET, url)
val version = json["version"] as? String
if (version == null) { deferred.reject(Exception("Missing snode version.")); return@Thread }
if (version == null) { deferred.reject(Exception("Missing snode version.")); return@queue }
if (version >= "2.0.7") {
deferred.resolve(Unit)
} else {
@ -99,7 +100,7 @@ object OnionRequestAPI {
} catch (exception: Exception) {
deferred.reject(exception)
}
}.start()
}
return deferred.promise
}
@ -313,10 +314,10 @@ object OnionRequestAPI {
return@success deferred.reject(exception)
}
val destinationSymmetricKey = result.destinationSymmetricKey
Thread {
ThreadUtils.queue {
try {
val json = HTTP.execute(HTTP.Verb.POST, url, body)
val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@Thread deferred.reject(Exception("Invalid JSON"))
val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@queue deferred.reject(Exception("Invalid JSON"))
val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext)
try {
val plaintext = AESGCM.decrypt(ivAndCiphertext, destinationSymmetricKey)
@ -326,7 +327,7 @@ object OnionRequestAPI {
if (statusCode == 406) {
@Suppress("NAME_SHADOWING") val body = mapOf( "result" to "Your clock is out of sync with the service node network." )
val exception = HTTPRequestFailedAtDestinationException(statusCode, body)
return@Thread deferred.reject(exception)
return@queue deferred.reject(exception)
} else if (json["body"] != null) {
@Suppress("NAME_SHADOWING") val body: Map<*, *>
if (json["body"] is Map<*, *>) {
@ -341,13 +342,13 @@ object OnionRequestAPI {
}
if (statusCode != 200) {
val exception = HTTPRequestFailedAtDestinationException(statusCode, body)
return@Thread deferred.reject(exception)
return@queue deferred.reject(exception)
}
deferred.resolve(body)
} else {
if (statusCode != 200) {
val exception = HTTPRequestFailedAtDestinationException(statusCode, json)
return@Thread deferred.reject(exception)
return@queue deferred.reject(exception)
}
deferred.resolve(json)
}
@ -360,7 +361,7 @@ object OnionRequestAPI {
} catch (exception: Exception) {
deferred.reject(exception)
}
}.start()
}
}.fail { exception ->
deferred.reject(exception)
}

View File

@ -5,6 +5,7 @@ import nl.komponents.kovenant.deferred
import org.session.libsignal.service.internal.util.JsonUtil
import org.session.libsession.utilities.AESGCM.EncryptionResult
import org.session.libsession.utilities.AESGCM
import org.session.libsession.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.toHexString
import java.nio.Buffer
import java.nio.ByteBuffer
@ -32,7 +33,7 @@ object OnionRequestEncryption {
*/
internal fun encryptPayloadForDestination(payload: Map<*, *>, destination: OnionRequestAPI.Destination): Promise<EncryptionResult, Exception> {
val deferred = deferred<EncryptionResult, Exception>()
Thread {
ThreadUtils.queue {
try {
// Wrapping isn't needed for file server or open group onion requests
when (destination) {
@ -52,7 +53,7 @@ object OnionRequestEncryption {
} catch (exception: Exception) {
deferred.reject(exception)
}
}.start()
}
return deferred.promise
}

View File

@ -7,24 +7,22 @@ import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import org.session.libsession.snode.utilities.getRandomElement
import org.session.libsession.utilities.ThreadUtils
import org.session.libsession.utilities.createContext
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.service.internal.util.Base64
import org.session.libsignal.service.loki.api.MessageWrapper
import org.session.libsignal.service.loki.api.utilities.HTTP
import org.session.libsignal.service.loki.utilities.createContext
import org.session.libsignal.service.loki.utilities.prettifiedDescription
import org.session.libsignal.service.loki.utilities.retryIfNeeded
import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope
import java.security.SecureRandom
object SnodeAPI {
val database = SnodeConfiguration.shared.storage
val broadcaster = SnodeConfiguration.shared.broadcaster
val sharedContext = Kovenant.createContext("LokiAPISharedContext")
val messageSendingContext = Kovenant.createContext("LokiAPIMessageSendingContext")
val messagePollingContext = Kovenant.createContext("LokiAPIMessagePollingContext")
val sharedContext = Kovenant.createContext()
val messageSendingContext = Kovenant.createContext()
val messagePollingContext = Kovenant.createContext()
internal var snodeFailureCount: MutableMap<Snode, Int> = mutableMapOf()
internal var snodePool: Set<Snode>
@ -57,7 +55,7 @@ object SnodeAPI {
return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey)
} else {
val deferred = deferred<Map<*, *>, Exception>()
Thread {
ThreadUtils.queue {
val payload = mapOf( "method" to method.rawValue, "params" to parameters )
try {
val json = HTTP.execute(HTTP.Verb.POST, url, payload)
@ -66,12 +64,12 @@ object SnodeAPI {
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException
if (httpRequestFailedException != null) {
val error = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey)
if (error != null) { return@Thread deferred.reject(exception) }
if (error != null) { return@queue deferred.reject(exception) }
}
Log.d("Loki", "Unhandled exception: $exception.")
deferred.reject(exception)
}
}.start()
}
return deferred.promise
}
}
@ -91,7 +89,7 @@ object SnodeAPI {
)
val deferred = deferred<Snode, Exception>()
deferred<org.session.libsignal.service.loki.api.Snode, Exception>(SnodeAPI.sharedContext)
Thread {
ThreadUtils.queue {
try {
val json = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true)
val intermediate = json["result"] as? Map<*, *>
@ -125,7 +123,7 @@ object SnodeAPI {
} catch (exception: Exception) {
deferred.reject(exception)
}
}.start()
}
return deferred.promise
} else {
return Promise.of(snodePool.getRandomElement())

View File

@ -1,11 +1,48 @@
@file:JvmName("PromiseUtilities")
package org.session.libsession.utilities
import nl.komponents.kovenant.Context
import nl.komponents.kovenant.Kovenant
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.jvm.asDispatcher
import org.session.libsignal.libsignal.logging.Log
import java.util.concurrent.Executors
import java.util.concurrent.TimeoutException
fun Kovenant.createContext(): Context {
return createContext {
callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
multipleCompletion = { v1, v2 ->
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
}
}
}
fun <V, E : Throwable> Promise<V, E>.get(defaultValue: V): V {
return try {
get()
} catch (e: Exception) {
defaultValue
}
}
fun <V, E : Throwable> Promise<V, E>.recover(callback: (exception: E) -> V): Promise<V, E> {
val deferred = deferred<V, E>()
success {
deferred.resolve(it)
}.fail {
try {
val value = callback(it)
deferred.resolve(value)
} catch (e: Throwable) {
deferred.reject(it)
}
}
return deferred.promise
}
fun <V, E> Promise<V, E>.successBackground(callback: (value: V) -> Unit): Promise<V, E> {
Thread {
try {

View File

@ -1,19 +0,0 @@
package org.session.libsession.utilities;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadUtil {
public static ExecutorService newDynamicSingleThreadedExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
executor.allowCoreThreadTimeOut(true);
return executor;
}
}

View File

@ -0,0 +1,26 @@
package org.session.libsession.utilities
import java.util.concurrent.*
object ThreadUtils {
internal val executorPool = Executors.newCachedThreadPool()
@JvmStatic
fun queue(target: Runnable) {
executorPool.execute(target)
}
fun queue(target: () -> Unit) {
executorPool.execute(target)
}
@JvmStatic
fun newDynamicSingleThreadedExecutor(): ExecutorService {
val executor = ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
LinkedBlockingQueue())
executor.allowCoreThreadTimeOut(true)
return executor
}
}