diff --git a/app/src/main/java/tech/relaycorp/letro/awala/AwalaManager.kt b/app/src/main/java/tech/relaycorp/letro/awala/AwalaManager.kt index 08a18d0f..39c53fb1 100644 --- a/app/src/main/java/tech/relaycorp/letro/awala/AwalaManager.kt +++ b/app/src/main/java/tech/relaycorp/letro/awala/AwalaManager.kt @@ -18,6 +18,7 @@ import tech.relaycorp.awaladroid.GatewayBindingException import tech.relaycorp.awaladroid.GatewayClient import tech.relaycorp.awaladroid.endpoint.FirstPartyEndpoint import tech.relaycorp.awaladroid.endpoint.InvalidThirdPartyEndpoint +import tech.relaycorp.awaladroid.endpoint.PrivateThirdPartyEndpoint import tech.relaycorp.awaladroid.endpoint.PublicThirdPartyEndpoint import tech.relaycorp.awaladroid.endpoint.ThirdPartyEndpoint import tech.relaycorp.awaladroid.messaging.OutgoingMessage @@ -43,6 +44,7 @@ interface AwalaManager { thirdPartyPublicKey: ByteArray, ) suspend fun getFirstPartyPublicKey(): String + suspend fun importPrivateThirdPartyAuth(auth: ByteArray): String } @OptIn(ExperimentalCoroutinesApi::class) @@ -72,9 +74,9 @@ class AwalaManagerImpl @Inject constructor( private var thirdPartyServerEndpoint: ThirdPartyEndpoint? = null init { - Log.i(TAG, "initializing") awalaSetupJob = awalaScope.launch { withContext(awalaThreadContext) { + Log.i(TAG, "Setting up Awala") Awala.setUp(context) checkIfAwalaAppInstalled() isAwalaSetUp.compareAndSet(false, true) @@ -142,6 +144,10 @@ class AwalaManagerImpl @Inject constructor( } } + override suspend fun importPrivateThirdPartyAuth(auth: ByteArray): String { + return PrivateThirdPartyEndpoint.import(auth).nodeId + } + private suspend fun loadFirstPartyEndpoint(): FirstPartyEndpoint { return withContext(awalaThreadContext) { val firstPartyEndpointNodeId = awalaRepository.getServerFirstPartyEndpointNodeId() @@ -203,6 +209,7 @@ class AwalaManagerImpl @Inject constructor( return withContext(awalaThreadContext) { try { GatewayClient.bind() + Log.i(TAG, "GatewayClient binded") configureAwala() } catch (exp: GatewayBindingException) { this@AwalaManagerImpl.isAwalaInstalledOnDevice = false @@ -216,6 +223,7 @@ class AwalaManagerImpl @Inject constructor( private suspend fun registerFirstPartyEndpointIfNeeded(): FirstPartyEndpoint? { return withContext(awalaThreadContext) { if (awalaRepository.getServerFirstPartyEndpointNodeId() != null) { + Log.i(TAG, "First party endpoint is already registred ${awalaRepository.getServerFirstPartyEndpointNodeId()}") startReceivingMessages() return@withContext null } diff --git a/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsDao.kt b/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsDao.kt index 49d4b288..61c98a1c 100644 --- a/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsDao.kt +++ b/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsDao.kt @@ -32,4 +32,7 @@ interface ContactsDao { @Delete suspend fun deleteContact(contact: Contact) + + @Query("SELECT * FROM $TABLE_NAME_CONTACTS WHERE contactEndpointId = :contactEndpointId") + suspend fun getContactsByContactEndpointId(contactEndpointId: String): List } diff --git a/app/src/main/java/tech/relaycorp/letro/di/AwalaModule.kt b/app/src/main/java/tech/relaycorp/letro/di/AwalaModule.kt index e844c3ec..1cd4339c 100644 --- a/app/src/main/java/tech/relaycorp/letro/di/AwalaModule.kt +++ b/app/src/main/java/tech/relaycorp/letro/di/AwalaModule.kt @@ -14,6 +14,7 @@ import tech.relaycorp.letro.awala.processor.AwalaMessageProcessor import tech.relaycorp.letro.awala.processor.AwalaMessageProcessorImpl import tech.relaycorp.letro.awala.processor.UnknownMessageProcessor import tech.relaycorp.letro.onboarding.registration.processor.RegistrationMessageProcessor +import tech.relaycorp.letro.pairing.processor.ContactPairingAuthorizationProcessor import tech.relaycorp.letro.pairing.processor.ContactPairingMatchProcessor import javax.inject.Singleton @@ -25,11 +26,13 @@ object AwalaModule { fun provideMessageProcessor( registrationMessageProcessor: RegistrationMessageProcessor, contactPairingMatchProcessor: ContactPairingMatchProcessor, + contactPairingAuthorizationProcessor: ContactPairingAuthorizationProcessor, unknownMessageProcessor: UnknownMessageProcessor, ): AwalaMessageProcessor { val processors = mapOf( MessageType.AccountCreationCompleted to registrationMessageProcessor, MessageType.ContactPairingMatch to contactPairingMatchProcessor, + MessageType.ContactPairingAuthorization to contactPairingAuthorizationProcessor, MessageType.Unknown to unknownMessageProcessor, ) return AwalaMessageProcessorImpl(processors) diff --git a/app/src/main/java/tech/relaycorp/letro/di/ContactsModule.kt b/app/src/main/java/tech/relaycorp/letro/di/ContactsModule.kt index 79ad02c8..0b18e780 100644 --- a/app/src/main/java/tech/relaycorp/letro/di/ContactsModule.kt +++ b/app/src/main/java/tech/relaycorp/letro/di/ContactsModule.kt @@ -8,8 +8,12 @@ import dagger.hilt.components.SingletonComponent import tech.relaycorp.letro.contacts.storage.ContactsDao import tech.relaycorp.letro.contacts.storage.ContactsRepository import tech.relaycorp.letro.contacts.storage.ContactsRepositoryImpl +import tech.relaycorp.letro.pairing.parser.ContactPairingAuthorizationParser +import tech.relaycorp.letro.pairing.parser.ContactPairingAuthorizationParserImpl import tech.relaycorp.letro.pairing.parser.ContactPairingMatchParser import tech.relaycorp.letro.pairing.parser.ContactPairingMatchParserImpl +import tech.relaycorp.letro.pairing.processor.ContactPairingAuthorizationProcessor +import tech.relaycorp.letro.pairing.processor.ContactPairingAuthorizationProcessorImpl import tech.relaycorp.letro.pairing.processor.ContactPairingMatchProcessor import tech.relaycorp.letro.pairing.processor.ContactPairingMatchProcessorImpl import tech.relaycorp.letro.storage.LetroDatabase @@ -43,5 +47,16 @@ object ContactsModule { fun bindContactPairingMatchProcessor( impl: ContactPairingMatchProcessorImpl, ): ContactPairingMatchProcessor + + @Binds + fun bindContactPairingAuthParser( + impl: ContactPairingAuthorizationParserImpl, + ): ContactPairingAuthorizationParser + + @Binds + @Singleton + fun bindContactPairingAuthProcessor( + impl: ContactPairingAuthorizationProcessorImpl, + ): ContactPairingAuthorizationProcessor } } diff --git a/app/src/main/java/tech/relaycorp/letro/home/LetroTabs.kt b/app/src/main/java/tech/relaycorp/letro/home/LetroTabs.kt index 3376d637..b72e87e7 100644 --- a/app/src/main/java/tech/relaycorp/letro/home/LetroTabs.kt +++ b/app/src/main/java/tech/relaycorp/letro/home/LetroTabs.kt @@ -59,7 +59,7 @@ fun LetroTabs( ) ScrollableTabRow( selectedTabIndex = uiState.currentTab, - containerColor = MaterialTheme.colorScheme.primary, + containerColor = LetroColor.SurfaceContainerHigh, contentColor = LetroColor.OnSurfaceContainerHigh, edgePadding = 9.dp, indicator = { diff --git a/app/src/main/java/tech/relaycorp/letro/pairing/dto/ContactPairingAuthorizationResponse.kt b/app/src/main/java/tech/relaycorp/letro/pairing/dto/ContactPairingAuthorizationResponse.kt new file mode 100644 index 00000000..93dbaafa --- /dev/null +++ b/app/src/main/java/tech/relaycorp/letro/pairing/dto/ContactPairingAuthorizationResponse.kt @@ -0,0 +1,15 @@ +package tech.relaycorp.letro.pairing.dto + +import tech.relaycorp.letro.awala.message.AwalaIncomingMessage +import tech.relaycorp.letro.awala.message.MessageType + +data class ContactPairingAuthorizationResponse( + val authData: ByteArray, +) + +data class ContactPairingAuthorizationIncomingMessage( + override val content: ContactPairingAuthorizationResponse, +) : AwalaIncomingMessage { + override val type: MessageType + get() = MessageType.ContactPairingAuthorization +} diff --git a/app/src/main/java/tech/relaycorp/letro/pairing/parser/ContactPairingAuthorizationParser.kt b/app/src/main/java/tech/relaycorp/letro/pairing/parser/ContactPairingAuthorizationParser.kt new file mode 100644 index 00000000..634b9c10 --- /dev/null +++ b/app/src/main/java/tech/relaycorp/letro/pairing/parser/ContactPairingAuthorizationParser.kt @@ -0,0 +1,19 @@ +package tech.relaycorp.letro.pairing.parser + +import tech.relaycorp.letro.awala.parser.AwalaMessageParser +import tech.relaycorp.letro.pairing.dto.ContactPairingAuthorizationIncomingMessage +import tech.relaycorp.letro.pairing.dto.ContactPairingAuthorizationResponse +import javax.inject.Inject + +interface ContactPairingAuthorizationParser : AwalaMessageParser + +class ContactPairingAuthorizationParserImpl @Inject constructor() : ContactPairingAuthorizationParser { + + override fun parse(content: ByteArray): ContactPairingAuthorizationIncomingMessage { + return ContactPairingAuthorizationIncomingMessage( + content = ContactPairingAuthorizationResponse( + authData = content, + ), + ) + } +} diff --git a/app/src/main/java/tech/relaycorp/letro/pairing/processor/ContactPairingAuthorizationProcessor.kt b/app/src/main/java/tech/relaycorp/letro/pairing/processor/ContactPairingAuthorizationProcessor.kt new file mode 100644 index 00000000..500eeb77 --- /dev/null +++ b/app/src/main/java/tech/relaycorp/letro/pairing/processor/ContactPairingAuthorizationProcessor.kt @@ -0,0 +1,33 @@ +package tech.relaycorp.letro.pairing.processor + +import tech.relaycorp.awaladroid.messaging.IncomingMessage +import tech.relaycorp.letro.awala.AwalaManager +import tech.relaycorp.letro.awala.processor.AwalaMessageProcessor +import tech.relaycorp.letro.contacts.model.ContactPairingStatus +import tech.relaycorp.letro.contacts.storage.ContactsDao +import tech.relaycorp.letro.pairing.dto.ContactPairingAuthorizationIncomingMessage +import tech.relaycorp.letro.pairing.parser.ContactPairingAuthorizationParser +import javax.inject.Inject + +interface ContactPairingAuthorizationProcessor : AwalaMessageProcessor + +class ContactPairingAuthorizationProcessorImpl @Inject constructor( + private val parser: ContactPairingAuthorizationParser, + private val contactsDao: ContactsDao, +) : ContactPairingAuthorizationProcessor { + + override suspend fun process(message: IncomingMessage, awalaManager: AwalaManager) { + val response = (parser.parse(message.content) as ContactPairingAuthorizationIncomingMessage).content + val nodeId = awalaManager.importPrivateThirdPartyAuth(response.authData) + + contactsDao.getContactsByContactEndpointId( + contactEndpointId = nodeId, + ).forEach { contactToUpdate -> + contactsDao.update( + contactToUpdate.copy( + status = ContactPairingStatus.COMPLETED, + ), + ) + } + } +}