Add github action workflow (#1016)

* Add build steps for github action

* Fix build commands

* Checkout submodule recursively and don't fail fast

* Fix mention view model tests

* Upload test result when failed

* Temporarily disable variants

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Remove deprecated properties

* Fixes up artifact uploading

* Fixes tests

* Fixes tests

* Huawei artifact matching and gradle caching

* PR trigger
pull/1712/head
SessionHero01 4 weeks ago committed by GitHub
parent e95fa6cc03
commit 9575db64fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,63 @@
name: Build and test
on:
push:
branches: [ "dev", "master" ]
pull_request:
types: [synchronize]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build_and_test:
runs-on: ubuntu-latest
strategy:
fail-fast: false # Continue with other matrix items if one fails
matrix:
variant: [ 'play', 'website', 'huawei' ]
build_type: [ 'debug' ]
include:
- variant: 'huawei'
extra_build_command_options: '-Phuawei=1'
steps:
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
.gradle
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '17'
- name: Build and test with Gradle
id: build
run: ./gradlew assemble${{ matrix.variant }}${{ matrix.build_type }} test${{ matrix.variant }}${{ matrix.build_type }}UnitTest ${{ matrix.extra_build_command_options }}
- name: Upload build reports regardless
if: always()
uses: actions/upload-artifact@v4
with:
name: build-reports-${{ matrix.variant }}-${{ matrix.build_type }}
path: app/build/reports
if-no-files-found: ignore
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: session-${{ matrix.variant }}-${{ matrix.build_type }}
path: app/build/outputs/apk/${{ matrix.variant }}/${{ matrix.build_type }}/*-universal*apk
if-no-files-found: error
compression-level: 0

@ -141,10 +141,7 @@ class MentionViewModel(
} }
val myId = if (openGroup != null) { val myId = if (openGroup != null) {
AccountId(IdPrefix.BLINDED, requireNotNull(storage.getUserBlindedAccountId(openGroup.publicKey)).hexString
SodiumUtilities.blindedKeyPair(openGroup.publicKey,
requireNotNull(storage.getUserED25519KeyPair()))!!.publicKey.asBytes)
.hexString
} else { } else {
requireNotNull(storage.getUserPublicKey()) requireNotNull(storage.getUserPublicKey())
} }

@ -228,6 +228,14 @@ open class Storage @Inject constructor(
override fun getUserED25519KeyPair(): KeyPair? { return KeyPairUtilities.getUserED25519KeyPair(context) } override fun getUserED25519KeyPair(): KeyPair? { return KeyPairUtilities.getUserED25519KeyPair(context) }
override fun getUserBlindedAccountId(serverPublicKey: String): AccountId? {
val userKeyPair = getUserED25519KeyPair() ?: return null
return AccountId(
IdPrefix.BLINDED,
SodiumUtilities.blindedKeyPair(serverPublicKey, userKeyPair)!!.publicKey.asBytes
)
}
override fun getUserProfile(): Profile { override fun getUserProfile(): Profile {
val displayName = usernameUtils.getCurrentUsername() val displayName = usernameUtils.getCurrentUsername()
val profileKey = ProfileKeyUtil.getProfileKey(context) val profileKey = ProfileKeyUtil.getProfileKey(context)

@ -1,15 +1,11 @@
package org.thoughtcrime.securesms package org.thoughtcrime.securesms
import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.runTest
import org.junit.Rule
open class BaseCoroutineTest { open class BaseCoroutineTest {
@get:Rule protected fun runBlockingTest(test: suspend TestScope.() -> Unit) = runTest {
var coroutinesTestRule = CoroutineTestRule() test()
}
protected fun runBlockingTest(test: suspend TestCoroutineScope.() -> Unit) =
coroutinesTestRule.runBlockingTest { test() }
} }

@ -1,50 +0,0 @@
package org.thoughtcrime.securesms
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description
/**
* Sets the main coroutines dispatcher to a [TestCoroutineScope] for unit testing. A
* [TestCoroutineScope] provides control over the execution of coroutines.
*
* Declare it as a JUnit Rule:
*
* ```
* @get:Rule
* var coroutineTestRule = CoroutineTestRule()
* ```
*
* Use it directly as a [TestCoroutineScope]:
*
* ```
* coroutineTestRule.pauseDispatcher()
* ...
* coroutineTestRule.resumeDispatcher()
* ...
* coroutineTestRule.runBlockingTest { }
* ...
*
*/
class CoroutineTestRule(
val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher(), TestCoroutineScope by TestCoroutineScope(dispatcher) {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
cleanupTestCoroutines()
Dispatchers.resetMain()
}
}

@ -2,24 +2,33 @@ package org.thoughtcrime.securesms
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher import org.junit.rules.TestWatcher
import org.junit.runner.Description import org.junit.runner.Description
import java.util.concurrent.atomic.AtomicBoolean
@ExperimentalCoroutinesApi @OptIn(ExperimentalCoroutinesApi::class)
class MainCoroutineRule(private val dispatcher: TestDispatcher = StandardTestDispatcher()) : class MainCoroutineRule() :
TestWatcher() { TestWatcher() {
companion object {
private val dispatcherSet = AtomicBoolean(false)
}
override fun starting(description: Description) { override fun starting(description: Description) {
super.starting(description) super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description) { // Set the main dispatcher to test dispatcher, however we shouldn't reset the main dispatcher
super.finished(description) // as some coroutine tasks spawn during the testing may try to resume on the main dispatcher,
Dispatchers.resetMain() // which will cause an exception if it has been reset.
// Right now there aren't good ways to force the coroutines run on other threads to behave
// correctly everytime so we'll just keep the main dispatcher as the test dispatcher globally.
if (dispatcherSet.compareAndSet(false, true)) {
Dispatchers.setMain(UnconfinedTestDispatcher(TestCoroutineScheduler()))
}
} }
} }

@ -23,6 +23,8 @@ import org.session.libsession.utilities.SSKEnvironment
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.BaseCoroutineTest
import org.thoughtcrime.securesms.BaseViewModelTest
import org.thoughtcrime.securesms.MainCoroutineRule import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryRadioOption import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryRadioOption
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCardData import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCardData
@ -44,9 +46,8 @@ private val GROUP_ADDRESS = Address.fromSerialized(GROUP_NUMBER)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@RunWith(MockitoJUnitRunner::class) @RunWith(MockitoJUnitRunner::class)
class DisappearingMessagesViewModelTest { class DisappearingMessagesViewModelTest : BaseViewModelTest() {
@ExperimentalCoroutinesApi
@get:Rule @get:Rule
var mainCoroutineRule = MainCoroutineRule() var mainCoroutineRule = MainCoroutineRule()

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2
import android.app.Application import android.app.Application
import com.goterl.lazysodium.utils.KeyPair import com.goterl.lazysodium.utils.KeyPair
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
@ -9,21 +10,29 @@ import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.CoreMatchers.nullValue import org.hamcrest.CoreMatchers.nullValue
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.Mockito.anyLong import org.mockito.Mockito.anyLong
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.kotlin.any import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever
import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.BaseViewModelTest import org.thoughtcrime.securesms.BaseViewModelTest
import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.repository.ConversationRepository import org.thoughtcrime.securesms.repository.ConversationRepository
import java.time.ZonedDateTime
class ConversationViewModelTest: BaseViewModelTest() { class ConversationViewModelTest: BaseViewModelTest() {
@get:Rule
val rule = MainCoroutineRule()
private val repository = mock<ConversationRepository>() private val repository = mock<ConversationRepository>()
private val storage = mock<Storage>() private val storage = mock<Storage>()
private val application = mock<Application>() private val application = mock<Application>()
@ -49,7 +58,10 @@ class ConversationViewModelTest: BaseViewModelTest() {
configFactory = mock(), configFactory = mock(),
groupManagerV2 = mock(), groupManagerV2 = mock(),
callManager = mock(), callManager = mock(),
legacyGroupDeprecationManager = mock(), legacyGroupDeprecationManager = mock {
on { deprecationState } doReturn MutableStateFlow(LegacyGroupDeprecationManager.DeprecationState.DEPRECATED)
on { deprecatedTime } doReturn MutableStateFlow(ZonedDateTime.now())
},
expiredGroupManager = mock(), expiredGroupManager = mock(),
usernameUtils = mock() usernameUtils = mock()
) )
@ -66,7 +78,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `should save draft message`() { fun `should save draft message`() = runBlockingTest {
val draft = "Hi there" val draft = "Hi there"
viewModel.saveDraft(draft) viewModel.saveDraft(draft)
@ -76,7 +88,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `should retrieve draft message`() { fun `should retrieve draft message`() = runBlockingTest {
val draft = "Hi there" val draft = "Hi there"
whenever(repository.getDraft(anyLong())).thenReturn(draft) whenever(repository.getDraft(anyLong())).thenReturn(draft)
@ -87,7 +99,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `should invite contacts`() { fun `should invite contacts`() = runBlockingTest {
val contacts = listOf<Recipient>() val contacts = listOf<Recipient>()
viewModel.inviteContacts(contacts) viewModel.inviteContacts(contacts)
@ -96,7 +108,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `should unblock contact recipient`() { fun `should unblock contact recipient`() = runBlockingTest {
whenever(recipient.isContactRecipient).thenReturn(true) whenever(recipient.isContactRecipient).thenReturn(true)
viewModel.unblock() viewModel.unblock()
@ -167,7 +179,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `open group recipient should have no blinded recipient`() { fun `open group recipient should have no blinded recipient`() = runBlockingTest {
whenever(recipient.isCommunityRecipient).thenReturn(true) whenever(recipient.isCommunityRecipient).thenReturn(true)
whenever(recipient.isCommunityOutboxRecipient).thenReturn(false) whenever(recipient.isCommunityOutboxRecipient).thenReturn(false)
whenever(recipient.isCommunityInboxRecipient).thenReturn(false) whenever(recipient.isCommunityInboxRecipient).thenReturn(false)
@ -175,14 +187,14 @@ class ConversationViewModelTest: BaseViewModelTest() {
} }
@Test @Test
fun `local recipient should have input and no blinded recipient`() { fun `local recipient should have input and no blinded recipient`() = runBlockingTest {
whenever(recipient.isLocalNumber).thenReturn(true) whenever(recipient.isLocalNumber).thenReturn(true)
assertThat(viewModel.shouldHideInputBar(), equalTo(false)) assertThat(viewModel.shouldHideInputBar(), equalTo(false))
assertThat(viewModel.blindedRecipient, nullValue()) assertThat(viewModel.blindedRecipient, nullValue())
} }
@Test @Test
fun `contact recipient should hide input bar if not accepting requests`() { fun `contact recipient should hide input bar if not accepting requests`() = runBlockingTest {
whenever(recipient.isCommunityInboxRecipient).thenReturn(true) whenever(recipient.isCommunityInboxRecipient).thenReturn(true)
val blinded = mock<Recipient> { val blinded = mock<Recipient> {
whenever(it.blocksCommunityMessageRequests).thenReturn(true) whenever(it.blocksCommunityMessageRequests).thenReturn(true)

@ -21,11 +21,13 @@ import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.open_groups.GroupMemberRole import org.session.libsession.messaging.open_groups.GroupMemberRole
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.AccountId
import org.thoughtcrime.securesms.BaseViewModelTest
import org.thoughtcrime.securesms.MainCoroutineRule import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.conversation.v2.mention.MentionViewModel import org.thoughtcrime.securesms.conversation.v2.mention.MentionViewModel
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
class MentionViewModelTest { class MentionViewModelTest : BaseViewModelTest() {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@get:Rule @get:Rule
val mainCoroutineRule = MainCoroutineRule() val mainCoroutineRule = MainCoroutineRule()
@ -37,16 +39,22 @@ class MentionViewModelTest {
private data class MemberInfo( private data class MemberInfo(
val name: String, val name: String,
val pubKey: String, val pubKey: String,
val roles: List<GroupMemberRole> val roles: List<GroupMemberRole>,
val isMe: Boolean
) )
private val myId = AccountId.fromString(
"151234567890123456789012345678901234567890123456789012345678901234"
)!!
private val threadMembers = listOf( private val threadMembers = listOf(
MemberInfo("Alice", "pubkey1", listOf(GroupMemberRole.ADMIN)), MemberInfo("You", myId.hexString, listOf(GroupMemberRole.STANDARD), isMe = true),
MemberInfo("Bob", "pubkey2", listOf(GroupMemberRole.STANDARD)), MemberInfo("Alice", "pubkey1", listOf(GroupMemberRole.ADMIN), isMe = false),
MemberInfo("Charlie", "pubkey3", listOf(GroupMemberRole.MODERATOR)), MemberInfo("Bob", "pubkey2", listOf(GroupMemberRole.STANDARD), isMe = false),
MemberInfo("David", "pubkey4", listOf(GroupMemberRole.HIDDEN_ADMIN)), MemberInfo("Charlie", "pubkey3", listOf(GroupMemberRole.MODERATOR), isMe = false),
MemberInfo("Eve", "pubkey5", listOf(GroupMemberRole.HIDDEN_MODERATOR)), MemberInfo("David", "pubkey4", listOf(GroupMemberRole.HIDDEN_ADMIN), isMe = false),
MemberInfo("李云海", "pubkey6", listOf(GroupMemberRole.ZOOMBIE)), MemberInfo("Eve", "pubkey5", listOf(GroupMemberRole.HIDDEN_MODERATOR), isMe = false),
MemberInfo("李云海", "pubkey6", listOf(GroupMemberRole.ZOOMBIE), isMe = false),
) )
private val memberContacts = threadMembers.map { m -> private val memberContacts = threadMembers.map { m ->
@ -106,7 +114,8 @@ class MentionViewModelTest {
}, },
storage = mock { storage = mock {
on { getOpenGroup(threadID) } doReturn openGroup on { getOpenGroup(threadID) } doReturn openGroup
}, on { getUserBlindedAccountId(any()) } doReturn myId
},
dispatcher = StandardTestDispatcher(), dispatcher = StandardTestDispatcher(),
configFactory = mock(), configFactory = mock(),
threadID = threadID, threadID = threadID,
@ -135,11 +144,11 @@ class MentionViewModelTest {
result as MentionViewModel.AutoCompleteState.Result result as MentionViewModel.AutoCompleteState.Result
assertThat(result.members).isEqualTo(threadMembers.mapIndexed { index, m -> assertThat(result.members).isEqualTo(threadMembers.mapIndexed { index, m ->
val name = val name = if (m.isMe) "You" else
memberContacts[index].displayName(Contact.ContactContext.OPEN_GROUP) memberContacts[index].displayName(Contact.ContactContext.OPEN_GROUP)
MentionViewModel.Candidate( MentionViewModel.Candidate(
MentionViewModel.Member(m.pubKey, name, m.roles.any { it.isModerator }, isMe = false), MentionViewModel.Member(m.pubKey, name, m.roles.any { it.isModerator }, isMe = m.isMe),
name, name,
0 0
) )
@ -157,8 +166,8 @@ class MentionViewModelTest {
.isInstanceOf(MentionViewModel.AutoCompleteState.Result::class.java) .isInstanceOf(MentionViewModel.AutoCompleteState.Result::class.java)
result as MentionViewModel.AutoCompleteState.Result result as MentionViewModel.AutoCompleteState.Result
assertThat(result.members[0].member.name).isEqualTo("Alice (pubk...key1)") assertThat(result.members[0].member.name).isEqualTo("Alice (pubkey1)")
assertThat(result.members[1].member.name).isEqualTo("Charlie (pubk...key3)") assertThat(result.members[1].member.name).isEqualTo("Charlie (pubkey3)")
} }
} }
} }

@ -1,14 +1,19 @@
package org.thoughtcrime.securesms.messagerequests package org.thoughtcrime.securesms.messagerequests
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.thoughtcrime.securesms.BaseViewModelTest import org.thoughtcrime.securesms.BaseViewModelTest
import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.repository.ConversationRepository import org.thoughtcrime.securesms.repository.ConversationRepository
class MessageRequestsViewModelTest : BaseViewModelTest() { class MessageRequestsViewModelTest : BaseViewModelTest() {
@get:Rule
val rule = MainCoroutineRule()
private val repository = mock(ConversationRepository::class.java) private val repository = mock(ConversationRepository::class.java)
private val viewModel: MessageRequestsViewModel by lazy { private val viewModel: MessageRequestsViewModel by lazy {

@ -22,7 +22,7 @@ navVersion=2.8.0-beta05
android.useAndroidX=true android.useAndroidX=true
appcompatVersion=1.6.1 appcompatVersion=1.6.1
coreVersion=1.13.1 coreVersion=1.13.1
coroutinesVersion=1.6.4 coroutinesVersion=1.9.0
curve25519Version=0.6.0 curve25519Version=0.6.0
jetpackHiltVersion=1.2.0 jetpackHiltVersion=1.2.0
daggerHiltVersion=2.55 daggerHiltVersion=2.55
@ -42,6 +42,8 @@ preferenceVersion=1.2.0
protobufVersion=4.29.3 protobufVersion=4.29.3
testCoreVersion=1.5.0 testCoreVersion=1.5.0
zxingVersion=3.5.3 zxingVersion=3.5.3
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false android.nonTransitiveRClass=false
android.nonFinalResIds=false android.nonFinalResIds=false
# Enable fast service loader to fix a crash in coroutine's test dispatcher set up
kotlinx.coroutines.fast.service.loader=true

@ -46,6 +46,7 @@ interface StorageProtocol {
fun getUserPublicKey(): String? fun getUserPublicKey(): String?
fun getUserED25519KeyPair(): KeyPair? fun getUserED25519KeyPair(): KeyPair?
fun getUserX25519KeyPair(): ECKeyPair fun getUserX25519KeyPair(): ECKeyPair
fun getUserBlindedAccountId(serverPublicKey: String): AccountId?
fun getUserProfile(): Profile fun getUserProfile(): Profile
fun setProfilePicture(recipient: Recipient, newProfilePicture: String?, newProfileKey: ByteArray?) fun setProfilePicture(recipient: Recipient, newProfilePicture: String?, newProfileKey: ByteArray?)
fun setBlocksCommunityMessageRequests(recipient: Recipient, blocksMessageRequests: Boolean) fun setBlocksCommunityMessageRequests(recipient: Recipient, blocksMessageRequests: Boolean)

@ -4,8 +4,6 @@ package org.session.libsession.snode
import android.os.SystemClock import android.os.SystemClock
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.NullNode
import com.fasterxml.jackson.databind.node.TextNode
import com.goterl.lazysodium.exceptions.SodiumException import com.goterl.lazysodium.exceptions.SodiumException
import com.goterl.lazysodium.interfaces.GenericHash import com.goterl.lazysodium.interfaces.GenericHash
import com.goterl.lazysodium.interfaces.PwHash import com.goterl.lazysodium.interfaces.PwHash
@ -18,6 +16,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.onTimeout
import kotlinx.coroutines.selects.select import kotlinx.coroutines.selects.select
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.all import nl.komponents.kovenant.all

@ -17,6 +17,11 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '11' jvmTarget = '11'
} }
buildFeatures {
buildConfig = true
}
namespace 'org.session.libsignal' namespace 'org.session.libsignal'
} }

Loading…
Cancel
Save