diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 00c4fdac84..e410e4cfa6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -77,5 +77,6 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration { .setRequired(PendingThreePidEntityFields.SEND_ATTEMPT, true) .addField(PendingThreePidEntityFields.SID, String::class.java) .setRequired(PendingThreePidEntityFields.SID, true) + .addField(PendingThreePidEntityFields.SUBMIT_URL, String::class.java) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PendingThreePidEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PendingThreePidEntity.kt index bf2f11dedd..2f5643d7bc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PendingThreePidEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PendingThreePidEntity.kt @@ -27,5 +27,6 @@ internal open class PendingThreePidEntity( var msisdn: String? = null, var clientSecret: String = "", var sendAttempt: Int = 0, - var sid: String = "" + var sid: String = "", + var submitUrl: String? = null ) : RealmObject() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddEmailResponse.kt similarity index 96% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddEmailResponse.kt index 109b3d5343..8654d7c5ba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddEmailResponse.kt @@ -20,7 +20,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class AddThreePidResponse( +internal data class AddEmailResponse( /** * Required. The session ID. Session IDs are opaque strings that must consist entirely * of the characters [0-9a-zA-Z.=_-]. Their length must not exceed 255 characters and they must not be empty. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnBody.kt new file mode 100644 index 0000000000..64c53f6729 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnBody.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.matrix.android.sdk.internal.session.profile + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class AddMsisdnBody( + /** + * Required. A unique string generated by the client, and used to identify the validation attempt. + * It must be a string consisting of the characters [0-9a-zA-Z.=_-]. Its length must not exceed + * 255 characters and it must not be empty. + */ + @Json(name = "client_secret") + val clientSecret: String, + + /** + * Required. The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in + * phone_number should be parsed as if it were dialled from. + */ + @Json(name = "country") + val country: String, + + /** + * Required. The phone number to validate. + */ + @Json(name = "phone_number") + val phoneNumber: String, + + /** + * Required. The server will only send an SMS if the send_attempt is a number greater than the most + * recent one which it has seen, scoped to that country + phone_number + client_secret triple. This + * is to avoid repeatedly sending the same SMS in the case of request retries between the POSTing user + * and the identity server. The client should increment this value if they desire a new SMS (e.g. a + * reminder) to be sent. + */ + @Json(name = "send_attempt") + val sendAttempt: Int +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnResponse.kt new file mode 100644 index 0000000000..b4c137b3a1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddMsisdnResponse.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.profile + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class AddMsisdnResponse( + /** + * Required. The session ID. Session IDs are opaque strings that must consist entirely of the characters [0-9a-zA-Z.=_-]. + * Their length must not exceed 255 characters and they must not be empty. + */ + @Json(name = "sid") + val sid: String, + + /** + * An optional field containing a URL where the client must submit the validation token to, with identical parameters to the Identity + * Service API's POST /validate/email/submitToken endpoint (without the requirement for an access token). + * The homeserver must send this token to the user (if applicable), who should then be prompted to provide it to the client. + * + * If this field is not present, the client can assume that verification will happen without the client's involvement provided + * the homeserver advertises this specification version in the /versions response (ie: r0.5.0). + */ + @Json(name = "submit_url") + val submitUrl: String? = null, + + /* ========================================================================================== + * It seems that the homeserver is sending more data, we may need it + * ========================================================================================== */ + + @Json(name = "msisdn") + val msisdn: String? = null, + + @Json(name = "intl_fmt") + val formattedMsisdn: String? = null, + + @Json(name = "success") + val success: Boolean? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt index 75c829c785..5b24961a49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/AddThreePidTask.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.profile +import com.google.i18n.phonenumbers.PhoneNumberUtil import com.zhuinden.monarchy.Monarchy import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.session.identity.ThreePid @@ -40,31 +41,76 @@ internal class DefaultAddThreePidTask @Inject constructor( private val eventBus: EventBus) : AddThreePidTask() { override suspend fun execute(params: Params) { + when (params.threePid) { + is ThreePid.Email -> addEmail(params.threePid) + is ThreePid.Msisdn -> addMsisdn(params.threePid) + } + } + + private suspend fun addEmail(threePid: ThreePid.Email) { val clientSecret = UUID.randomUUID().toString() val sendAttempt = 1 - val result = when (params.threePid) { - is ThreePid.Email -> - executeRequest(eventBus) { - val body = AddEmailBody( - email = params.threePid.email, - sendAttempt = sendAttempt, - clientSecret = clientSecret - ) - apiCall = profileAPI.addEmail(body) - } - is ThreePid.Msisdn -> TODO() + + val result = executeRequest(eventBus) { + val body = AddEmailBody( + clientSecret = clientSecret, + email = threePid.email, + sendAttempt = sendAttempt + ) + apiCall = profileAPI.addEmail(body) } // Store as a pending three pid monarchy.awaitTransaction { realm -> PendingThreePid( - threePid = params.threePid, + threePid = threePid, clientSecret = clientSecret, sendAttempt = sendAttempt, - sid = result.sid + sid = result.sid, + submitUrl = null ) .let { pendingThreePidMapper.map(it) } .let { realm.copyToRealm(it) } } } + + private suspend fun addMsisdn(threePid: ThreePid.Msisdn) { + val clientSecret = UUID.randomUUID().toString() + val sendAttempt = 1 + + // Get country code from the phone number + val phoneNumber = threePid.msisdn + + val parsedNumber = PhoneNumberUtil.getInstance().parse(phoneNumber, null) + val countryCode = parsedNumber.countryCode + + val result = executeRequest(eventBus) { + val body = AddMsisdnBody( + clientSecret = clientSecret, + country = countryCode.asString(), // TODO Convert to String, + phoneNumber = parsedNumber.nationalNumber.toString(), + sendAttempt = sendAttempt + ) + apiCall = profileAPI.addMsisdn(body) + } + + // Store as a pending three pid + monarchy.awaitTransaction { realm -> + PendingThreePid( + threePid = threePid, + clientSecret = clientSecret, + sendAttempt = sendAttempt, + sid = result.sid, + submitUrl = result.submitUrl + ) + .let { pendingThreePidMapper.map(it) } + .let { realm.copyToRealm(it) } + } + } + + private fun Int.asString(): String { + // TODO + return "" + } } + diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePid.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePid.kt index d9c3a5d656..ea3d0f1ee8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePid.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePid.kt @@ -22,6 +22,9 @@ internal data class PendingThreePid( val threePid: ThreePid, val clientSecret: String, val sendAttempt: Int, - val sid: String + // For Msisdn and Email + val sid: String, + // For Msisdn only + val submitUrl: String? ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePidMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePidMapper.kt index c9d6381bd3..b1877027ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePidMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/PendingThreePidMapper.kt @@ -29,7 +29,8 @@ internal class PendingThreePidMapper @Inject constructor() { ?: error("Invalid data"), clientSecret = entity.clientSecret, sendAttempt = entity.sendAttempt, - sid = entity.sid + sid = entity.sid, + submitUrl = entity.submitUrl ) } @@ -39,7 +40,8 @@ internal class PendingThreePidMapper @Inject constructor() { msisdn = domain.threePid.takeIf { it is ThreePid.Msisdn }?.value, clientSecret = domain.clientSecret, sendAttempt = domain.sendAttempt, - sid = domain.sid + sid = domain.sid, + submitUrl = domain.submitUrl ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ProfileAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ProfileAPI.kt index 125b1a47e6..cfd965e23f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ProfileAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/ProfileAPI.kt @@ -75,7 +75,13 @@ internal interface ProfileAPI { * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-email-requesttoken */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/3pid/email/requestToken") - fun addEmail(@Body body: AddEmailBody): Call + fun addEmail(@Body body: AddEmailBody): Call + + /** + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-msisdn-requesttoken + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/3pid/msisdn/requestToken") + fun addMsisdn(@Body body: AddMsisdnBody): Call /** * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-add diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt index 27844f0c22..8ca506e819 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt @@ -30,6 +30,7 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.isEmail +import im.vector.app.core.extensions.isMsisdn import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment @@ -123,6 +124,11 @@ class ThreePidsSettingsFragment @Inject constructor( return } + if (!msisdn.isMsisdn()) { + viewModel.handle(ThreePidsSettingsAction.ChangeState(ThreePidsSettingsState.AddingPhoneNumber(getString(R.string.login_msisdn_error_other)))) + return + } + viewModel.handle(ThreePidsSettingsAction.AddThreePid(ThreePid.Msisdn(safeMsisdn))) }