diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt index aedb813735..8f8967e8fb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.identity -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * Provides access to the identity server configuration and services identity server can provide */ @@ -40,55 +37,55 @@ interface IdentityService { * See https://matrix.org/docs/spec/identity_service/latest#status-check * RiotX SDK only supports identity server API v2 */ - fun isValidIdentityServer(url: String, callback: MatrixCallback): Cancelable + suspend fun isValidIdentityServer(url: String) /** * Update the identity server url. * If successful, any previous identity server will be disconnected. * In case of error, any previous identity server will remain configured. * @param url the new url. - * @param callback will notify the user if change is successful. The String will be the final url of the identity server. + * @return The String will be the final url of the identity server. * The SDK can prepend "https://" for instance. */ - fun setNewIdentityServer(url: String, callback: MatrixCallback): Cancelable + suspend fun setNewIdentityServer(url: String): String /** * Disconnect (logout) from the current identity server */ - fun disconnect(callback: MatrixCallback): Cancelable + suspend fun disconnect() /** * This will ask the identity server to send an email or an SMS to let the user confirm he owns the ThreePid */ - fun startBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable + suspend fun startBindThreePid(threePid: ThreePid) /** * This will cancel a pending binding of threePid. */ - fun cancelBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable + suspend fun cancelBindThreePid(threePid: ThreePid) /** * This will ask the identity server to send an new email or a new SMS to let the user confirm he owns the ThreePid */ - fun sendAgainValidationCode(threePid: ThreePid, callback: MatrixCallback): Cancelable + suspend fun sendAgainValidationCode(threePid: ThreePid) /** * Submit the code that the identity server has sent to the user (in email or SMS) * Once successful, you will have to call [finalizeBindThreePid] * @param code the code sent to the user */ - fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback): Cancelable + suspend fun submitValidationToken(threePid: ThreePid, code: String) /** * This will perform the actual association of ThreePid and Matrix account */ - fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable + suspend fun finalizeBindThreePid(threePid: ThreePid) /** * Unbind a threePid * The request will actually be done on the homeserver */ - fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable + suspend fun unbindThreePid(threePid: ThreePid) /** * Search MatrixId of users providing email and phone numbers @@ -96,7 +93,7 @@ interface IdentityService { * Application has to explicitly ask for the user consent, and the answer can be stored using [setUserConsent] * Please see https://support.google.com/googleplay/android-developer/answer/9888076?hl=en for more details. */ - fun lookUp(threePids: List, callback: MatrixCallback>): Cancelable + suspend fun lookUp(threePids: List): List /** * Return the current user consent for the current identity server, which has been stored using [setUserConsent]. @@ -120,9 +117,9 @@ interface IdentityService { * A lookup will be performed, but also pending binding state will be restored * * @param threePids the list of threePid the user owns (retrieved form the homeserver) - * @param callback onSuccess will be called with a map of ThreePid -> SharedState + * @return a map of ThreePid -> SharedState */ - fun getShareStatus(threePids: List, callback: MatrixCallback>): Cancelable + suspend fun getShareStatus(threePids: List): Map fun addListener(listener: IdentityServiceListener) fun removeListener(listener: IdentityServiceListener) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index 948e387cb1..f5391d6cdb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import dagger.Lazy -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.Failure @@ -33,8 +32,6 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.internal.di.AuthenticatedIdentity import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.extensions.observeNotNull @@ -49,8 +46,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.IdentitySe import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.launchToCallback import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.ensureProtocol import kotlinx.coroutines.withContext @@ -83,8 +78,7 @@ internal class DefaultIdentityService @Inject constructor( private val identityApiProvider: IdentityApiProvider, private val accountDataDataSource: AccountDataDataSource, private val homeServerCapabilitiesService: HomeServerCapabilitiesService, - private val sessionParams: SessionParams, - private val taskExecutor: TaskExecutor + private val sessionParams: SessionParams ) : IdentityService, SessionLifecycleObserver { private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } @@ -136,101 +130,81 @@ internal class DefaultIdentityService @Inject constructor( return identityStore.getIdentityData()?.identityServerUrl } - override fun startBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable { + override suspend fun startBindThreePid(threePid: ThreePid) { if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) { - callback.onFailure(IdentityServiceError.OutdatedHomeServer) - return NoOpCancellable + throw IdentityServiceError.OutdatedHomeServer } - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - identityRequestTokenForBindingTask.execute(IdentityRequestTokenForBindingTask.Params(threePid, false)) - } + identityRequestTokenForBindingTask.execute(IdentityRequestTokenForBindingTask.Params(threePid, false)) } - override fun cancelBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - identityStore.deletePendingBinding(threePid) - } + override suspend fun cancelBindThreePid(threePid: ThreePid) { + identityStore.deletePendingBinding(threePid) } - override fun sendAgainValidationCode(threePid: ThreePid, callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - identityRequestTokenForBindingTask.execute(IdentityRequestTokenForBindingTask.Params(threePid, true)) - } + override suspend fun sendAgainValidationCode(threePid: ThreePid) { + identityRequestTokenForBindingTask.execute(IdentityRequestTokenForBindingTask.Params(threePid, true)) } - override fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable { + override suspend fun finalizeBindThreePid(threePid: ThreePid) { if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) { - callback.onFailure(IdentityServiceError.OutdatedHomeServer) - return NoOpCancellable + throw IdentityServiceError.OutdatedHomeServer } - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - bindThreePidsTask.execute(BindThreePidsTask.Params(threePid)) - } + bindThreePidsTask.execute(BindThreePidsTask.Params(threePid)) } - override fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - submitTokenForBindingTask.execute(IdentitySubmitTokenForBindingTask.Params(threePid, code)) - } + override suspend fun submitValidationToken(threePid: ThreePid, code: String) { + submitTokenForBindingTask.execute(IdentitySubmitTokenForBindingTask.Params(threePid, code)) } - override fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback): Cancelable { + override suspend fun unbindThreePid(threePid: ThreePid) { if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) { - callback.onFailure(IdentityServiceError.OutdatedHomeServer) - return NoOpCancellable + throw IdentityServiceError.OutdatedHomeServer } - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - unbindThreePidsTask.execute(UnbindThreePidsTask.Params(threePid)) - } + unbindThreePidsTask.execute(UnbindThreePidsTask.Params(threePid)) } - override fun isValidIdentityServer(url: String, callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - val api = retrofitFactory.create(unauthenticatedOkHttpClient, url).create(IdentityAuthAPI::class.java) + override suspend fun isValidIdentityServer(url: String) { + val api = retrofitFactory.create(unauthenticatedOkHttpClient, url).create(IdentityAuthAPI::class.java) - identityPingTask.execute(IdentityPingTask.Params(api)) - } + identityPingTask.execute(IdentityPingTask.Params(api)) } - override fun disconnect(callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - identityDisconnectTask.execute(Unit) + override suspend fun disconnect() { + identityDisconnectTask.execute(Unit) - identityStore.setUrl(null) - updateIdentityAPI(null) - updateAccountData(null) - } + identityStore.setUrl(null) + updateIdentityAPI(null) + updateAccountData(null) } - override fun setNewIdentityServer(url: String, callback: MatrixCallback): Cancelable { + override suspend fun setNewIdentityServer(url: String): String { val urlCandidate = url.ensureProtocol() - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - val current = getCurrentIdentityServerUrl() - if (urlCandidate == current) { - // Nothing to do - Timber.d("Same URL, nothing to do") - } else { - // Disconnect previous one if any, first, because the token will change. - // In case of error when configuring the new identity server, this is not a big deal, - // we will ask for a new token on the previous Identity server - runCatching { identityDisconnectTask.execute(Unit) } - .onFailure { Timber.w(it, "Unable to disconnect identity server") } + val current = getCurrentIdentityServerUrl() + if (urlCandidate == current) { + // Nothing to do + Timber.d("Same URL, nothing to do") + } else { + // Disconnect previous one if any, first, because the token will change. + // In case of error when configuring the new identity server, this is not a big deal, + // we will ask for a new token on the previous Identity server + runCatching { identityDisconnectTask.execute(Unit) } + .onFailure { Timber.w(it, "Unable to disconnect identity server") } - // Try to get a token - val token = getNewIdentityServerToken(urlCandidate) + // Try to get a token + val token = getNewIdentityServerToken(urlCandidate) - identityStore.setUrl(urlCandidate) - identityStore.setToken(token) - updateIdentityAPI(urlCandidate) + identityStore.setUrl(urlCandidate) + identityStore.setToken(token) + updateIdentityAPI(urlCandidate) - updateAccountData(urlCandidate) - } - urlCandidate + updateAccountData(urlCandidate) } + + return urlCandidate } private suspend fun updateAccountData(url: String?) { @@ -252,45 +226,38 @@ internal class DefaultIdentityService @Inject constructor( identityStore.setUserConsent(newValue) } - override fun lookUp(threePids: List, callback: MatrixCallback>): Cancelable { + override suspend fun lookUp(threePids: List): List { if (!getUserConsent()) { - callback.onFailure(IdentityServiceError.UserConsentNotProvided) - return NoOpCancellable + throw IdentityServiceError.UserConsentNotProvided } if (threePids.isEmpty()) { - callback.onSuccess(emptyList()) - return NoOpCancellable + return emptyList() } - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - lookUpInternal(true, threePids) - } + return lookUpInternal(true, threePids) } - override fun getShareStatus(threePids: List, callback: MatrixCallback>): Cancelable { + override suspend fun getShareStatus(threePids: List): Map { // Note: we do not require user consent here, because it is used for emails and phone numbers that the user has already sent // to the home server, and not emails and phone numbers from the contact book of the user if (threePids.isEmpty()) { - callback.onSuccess(emptyMap()) - return NoOpCancellable + return emptyMap() } - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { - val lookupResult = lookUpInternal(true, threePids) + val lookupResult = lookUpInternal(true, threePids) - threePids.associateWith { threePid -> - // If not in lookup result, check if there is a pending binding - if (lookupResult.firstOrNull { it.threePid == threePid } == null) { - if (identityStore.getPendingBinding(threePid) == null) { - SharedState.NOT_SHARED - } else { - SharedState.BINDING_IN_PROGRESS - } + return threePids.associateWith { threePid -> + // If not in lookup result, check if there is a pending binding + if (lookupResult.firstOrNull { it.threePid == threePid } == null) { + if (identityStore.getPendingBinding(threePid) == null) { + SharedState.NOT_SHARED } else { - SharedState.SHARED + SharedState.BINDING_IN_PROGRESS } + } else { + SharedState.SHARED } } } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index 05af63d7ba..cfbdef8ffb 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -33,9 +33,7 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.identity.FoundThreePid import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.ThreePid import timber.log.Timber @@ -101,56 +99,56 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted } } - private fun performLookup(data: List) { + private fun performLookup(contacts: List) { if (!session.identityService().getUserConsent()) { return } viewModelScope.launch { - val threePids = data.flatMap { contact -> + val threePids = contacts.flatMap { contact -> contact.emails.map { ThreePid.Email(it.email) } + contact.msisdns.map { ThreePid.Msisdn(it.phoneNumber) } } - session.identityService().lookUp(threePids, object : MatrixCallback> { - override fun onFailure(failure: Throwable) { - Timber.w(failure, "Unable to perform the lookup") - // Should not happen, but just to be sure - if (failure is IdentityServiceError.UserConsentNotProvided) { - setState { - copy(userConsent = false) - } - } - } - - override fun onSuccess(data: List) { - mappedContacts = allContacts.map { contactModel -> - contactModel.copy( - emails = contactModel.emails.map { email -> - email.copy( - matrixId = data - .firstOrNull { foundThreePid -> foundThreePid.threePid.value == email.email } - ?.matrixId - ) - }, - msisdns = contactModel.msisdns.map { msisdn -> - msisdn.copy( - matrixId = data - .firstOrNull { foundThreePid -> foundThreePid.threePid.value == msisdn.phoneNumber } - ?.matrixId - ) - } - ) - } + val data = try { + session.identityService().lookUp(threePids) + } catch (failure: Throwable) { + Timber.w(failure, "Unable to perform the lookup") + // Should not happen, but just to be sure + if (failure is IdentityServiceError.UserConsentNotProvided) { setState { - copy( - isBoundRetrieved = true - ) + copy(userConsent = false) } - - updateFilteredMappedContacts() } - }) + return@launch + } + + mappedContacts = allContacts.map { contactModel -> + contactModel.copy( + emails = contactModel.emails.map { email -> + email.copy( + matrixId = data + .firstOrNull { foundThreePid -> foundThreePid.threePid.value == email.email } + ?.matrixId + ) + }, + msisdns = contactModel.msisdns.map { msisdn -> + msisdn.copy( + matrixId = data + .firstOrNull { foundThreePid -> foundThreePid.threePid.value == msisdn.phoneNumber } + ?.matrixId + ) + } + ) + } + + setState { + copy( + isBoundRetrieved = true + ) + } + + updateFilteredMappedContacts() } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index bf2defafa1..11fd796534 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -35,7 +35,6 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx class DiscoverySettingsViewModel @AssistedInject constructor( @@ -123,7 +122,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { session.identityService().disconnect(it) } + session.identityService().disconnect() setState { copy( identityServer = Success(null), @@ -141,9 +140,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - val data = awaitCallback { - session.identityService().setNewIdentityServer(action.url, it) - } + val data = session.identityService().setNewIdentityServer(action.url) setState { copy( identityServer = Success(data), @@ -163,7 +160,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { identityService.startBindThreePid(action.threePid, it) } + identityService.startBindThreePid(action.threePid) changeThreePidState(action.threePid, Success(SharedState.BINDING_IN_PROGRESS)) } catch (failure: Throwable) { _viewEvents.post(DiscoverySettingsViewEvents.Failure(failure)) @@ -240,7 +237,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { identityService.unbindThreePid(threePid, it) } + identityService.unbindThreePid(threePid) changeThreePidState(threePid, Success(SharedState.NOT_SHARED)) } catch (failure: Throwable) { _viewEvents.post(DiscoverySettingsViewEvents.Failure(failure)) @@ -256,7 +253,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { identityService.unbindThreePid(threePid, it) } + identityService.unbindThreePid(threePid) changeThreePidState(threePid, Success(SharedState.NOT_SHARED)) } catch (failure: Throwable) { _viewEvents.post(DiscoverySettingsViewEvents.Failure(failure)) @@ -268,7 +265,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( private fun cancelBinding(action: DiscoverySettingsAction.CancelBinding) { viewModelScope.launch { try { - awaitCallback { identityService.cancelBindThreePid(action.threePid, it) } + identityService.cancelBindThreePid(action.threePid) changeThreePidState(action.threePid, Success(SharedState.NOT_SHARED)) changeThreePidSubmitState(action.threePid, Uninitialized) } catch (failure: Throwable) { @@ -304,9 +301,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - val data = awaitCallback> { - identityService.getShareStatus(threePids, it) - } + val data = identityService.getShareStatus(threePids) setState { copy( emailList = Success(data.filter { it.key is ThreePid.Email }.toPidInfoList()), @@ -346,9 +341,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { - identityService.submitValidationToken(action.threePid, action.code, it) - } + identityService.submitValidationToken(action.threePid, action.code) changeThreePidSubmitState(action.threePid, Uninitialized) finalizeBind3pid(DiscoverySettingsAction.FinalizeBind3pid(action.threePid), true) } catch (failure: Throwable) { @@ -371,7 +364,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { identityService.finalizeBindThreePid(threePid, it) } + identityService.finalizeBindThreePid(threePid) changeThreePidSubmitState(action.threePid, Uninitialized) changeThreePidState(action.threePid, Success(SharedState.SHARED)) } catch (failure: Throwable) { diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index 9455b1bff4..08632a2bd1 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -33,7 +33,6 @@ import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.terms.TermsService -import org.matrix.android.sdk.internal.util.awaitCallback import java.net.UnknownHostException class SetIdentityServerViewModel @AssistedInject constructor( @@ -97,9 +96,7 @@ class SetIdentityServerViewModel @AssistedInject constructor( viewModelScope.launch { try { // First ping the identity server v2 API - awaitCallback { - mxSession.identityService().isValidIdentityServer(baseUrl, it) - } + mxSession.identityService().isValidIdentityServer(baseUrl) // Ok, next step checkTerms(baseUrl) } catch (failure: Throwable) {