From c0fa259b4065cee8a0179e92ca6e151ed913b206 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 29 Feb 2020 10:29:49 +0100 Subject: [PATCH 01/16] Add a setting to prevent screenshot of the application, disabled by default (#1027) --- CHANGES.md | 1 + .../riotx/core/platform/VectorBaseActivity.kt | 6 +++ .../features/settings/VectorPreferences.kt | 13 +++++- vector/src/main/res/values/strings_riotX.xml | 6 +++ .../xml/vector_settings_security_privacy.xml | 42 ++++++++++++------- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a1748f3623..d71470c39c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -37,6 +37,7 @@ Build 🧱: - Compile with Android SDK 29 (Android Q) Other changes: + - Add a setting to prevent screenshots of the application, disabled by default (#1027) - Increase File Logger capacities ( + use dev log preferences) Changes in RiotX 0.18.1 (2020-03-17) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index ea954ecf27..08cf8e57e1 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -23,6 +23,7 @@ import android.os.Parcelable import android.view.Menu import android.view.MenuItem import android.view.View +import android.view.WindowManager import androidx.annotation.AttrRes import androidx.annotation.LayoutRes import androidx.annotation.MainThread @@ -183,6 +184,11 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { handleGlobalError(it) } + // Set flag FLAG_SECURE + if (vectorPreferences.useFlagSecure()) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + doBeforeSetContentView() if (getLayoutRes() != -1) { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt index faa48d5686..f0a5a8ace8 100755 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt @@ -150,6 +150,9 @@ class VectorPreferences @Inject constructor(private val context: Context) { const val SETTINGS_USE_RAGE_SHAKE_KEY = "SETTINGS_USE_RAGE_SHAKE_KEY" const val SETTINGS_RAGE_SHAKE_DETECTION_THRESHOLD_KEY = "SETTINGS_RAGE_SHAKE_DETECTION_THRESHOLD_KEY" + // Security + const val SETTINGS_SECURITY_USE_FLAG_SECURE = "SETTINGS_SECURITY_USE_FLAG_SECURE" + // other const val SETTINGS_MEDIA_SAVING_PERIOD_KEY = "SETTINGS_MEDIA_SAVING_PERIOD_KEY" private const val SETTINGS_MEDIA_SAVING_PERIOD_SELECTED_KEY = "SETTINGS_MEDIA_SAVING_PERIOD_SELECTED_KEY" @@ -199,7 +202,8 @@ class VectorPreferences @Inject constructor(private val context: Context) { SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY, SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY, - SETTINGS_USE_RAGE_SHAKE_KEY + SETTINGS_USE_RAGE_SHAKE_KEY, + SETTINGS_SECURITY_USE_FLAG_SECURE ) } @@ -746,4 +750,11 @@ class VectorPreferences @Inject constructor(private val context: Context) { fun displayAllEvents(): Boolean { return defaultPrefs.getBoolean(SETTINGS_DISPLAY_ALL_EVENTS_KEY, false) } + + /** + * The user does not allow screenshots of the application + */ + fun useFlagSecure(): Boolean { + return defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_FLAG_SECURE, false) + } } diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 00bf65e121..c55aaf4687 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -14,6 +14,12 @@ + + Prevent screenshots of the application + Enabling this setting adds the FLAG_SECURE to all Activities. Restart the application for the change to take effect. + + + diff --git a/vector/src/main/res/xml/vector_settings_security_privacy.xml b/vector/src/main/res/xml/vector_settings_security_privacy.xml index 19bc340500..1002c16782 100644 --- a/vector/src/main/res/xml/vector_settings_security_privacy.xml +++ b/vector/src/main/res/xml/vector_settings_security_privacy.xml @@ -6,36 +6,35 @@ + tools:isPreferenceVisible="true"> + tools:icon="@drawable/ic_shield_trusted" + tools:summary="@string/encryption_information_dg_xsigning_complete" /> - - - + + + - - - + + + - - - + + + + android:title="@string/encryption_never_send_to_unverified_devices_title" /> @@ -85,4 +84,15 @@ + + + + + + \ No newline at end of file From f00db49bda0421e9d21a850f3a80a2ac7e6286e9 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 11 Mar 2020 00:16:37 +0300 Subject: [PATCH 02/16] Change password function implemented. Fixes #528 --- CHANGES.md | 1 + .../android/api/auth/AuthenticationService.kt | 6 ++ .../api/auth/password/PasswordWizard.kt | 28 ++++++ .../matrix/android/api/failure/Extensions.kt | 5 ++ .../matrix/android/internal/auth/AuthAPI.kt | 7 ++ .../auth/DefaultAuthenticationService.kt | 19 ++++ .../auth/data/UpdatePasswordParams.kt | 42 +++++++++ .../auth/password/DefaultPasswordWizard.kt | 87 +++++++++++++++++++ .../crypto/model/rest/UserPasswordAuth.kt | 2 +- .../im/vector/riotx/core/di/FragmentModule.kt | 6 ++ .../settings/VectorSettingsGeneralFragment.kt | 32 ++++++- 11 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt diff --git a/CHANGES.md b/CHANGES.md index e7f920d788..19c7b7e618 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ Changes in RiotX 0.19.0 (2020-XX-XX) =================================================== Features ✨: + - Change password (#528) - Cross-Signing | Support SSSS secret sharing (#944) - Cross-Signing | Verify new session from existing session (#1134) - Cross-Signing | Bootstraping cross signing with 4S from mobile (#985) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt index 140d1c259f..04e53e79f1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.LoginFlowResult import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.login.LoginWizard +import im.vector.matrix.android.api.auth.password.PasswordWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.util.Cancelable @@ -89,4 +90,9 @@ interface AuthenticationService { fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, credentials: Credentials, callback: MatrixCallback): Cancelable + + /** + * Return a PasswordWizard, to update password. + */ + fun getPasswordWizard(): PasswordWizard } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt new file mode 100644 index 0000000000..cd41d79b0a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.api.auth.password + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable + +interface PasswordWizard { + + /** + * Ask the homeserver to update the password with the provided new password. + */ + fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback): Cancelable +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt index 5dfb0eab9b..260b3bb8a4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt @@ -31,3 +31,8 @@ fun Throwable.shouldBeRetried(): Boolean { return this is Failure.NetworkConnection || (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED) } + +fun Throwable.isInvalidPassword(): Boolean { + return this is Failure.ServerError + && error.message == "Invalid password" +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt index 2f03c99421..f4258acf54 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.RiotConfig import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed +import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse import im.vector.matrix.android.internal.auth.registration.RegistrationParams @@ -102,4 +103,10 @@ internal interface AuthAPI { */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") fun resetPasswordMailConfirmed(@Body params: ResetPasswordMailConfirmed): Call + + /** + * Ask the homeserver to update the password with the provided new password. + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") + fun updatePassword(@Body params: UpdatePasswordParams): Call } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt index 85c2cdbf3d..1e869dd1d8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt @@ -28,6 +28,7 @@ import im.vector.matrix.android.api.auth.data.Versions import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk import im.vector.matrix.android.api.auth.data.isSupportedBySdk import im.vector.matrix.android.api.auth.login.LoginWizard +import im.vector.matrix.android.api.auth.password.PasswordWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.Session @@ -37,6 +38,7 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse import im.vector.matrix.android.internal.auth.data.RiotConfig import im.vector.matrix.android.internal.auth.db.PendingSessionData import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard +import im.vector.matrix.android.internal.auth.password.DefaultPasswordWizard import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.network.RetrofitFactory @@ -66,6 +68,7 @@ internal class DefaultAuthenticationService @Inject constructor( private var currentLoginWizard: LoginWizard? = null private var currentRegistrationWizard: RegistrationWizard? = null + private var currentPasswordWizard: PasswordWizard? = null override fun hasAuthenticatedSessions(): Boolean { return sessionParamsStore.getLast() != null @@ -221,6 +224,22 @@ internal class DefaultAuthenticationService @Inject constructor( } } + override fun getPasswordWizard(): PasswordWizard { + return currentPasswordWizard + ?: let { + sessionParamsStore.getLast()?.homeServerConnectionConfig?.let { + DefaultPasswordWizard( + okHttpClient, + retrofitFactory, + coroutineDispatchers, + it + ).also { + currentPasswordWizard = it + } + } ?: error("HomeServerConnectionConfig is null") + } + } + override fun cancelPendingLoginOrRegistration() { currentLoginWizard = null currentRegistrationWizard = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt new file mode 100644 index 0000000000..250bd9ec2e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.auth.data + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth + +/** + * Class to pass request parameters to update the password. + */ +@JsonClass(generateAdapter = true) +internal data class UpdatePasswordParams( + @Json(name = "auth") + val auth: UserPasswordAuth? = null, + + @Json(name = "new_password") + val newPassword: String? = null +) { + companion object { + fun create(sessionId: String, userId: String, oldPassword: String, newPassword: String): UpdatePasswordParams { + return UpdatePasswordParams( + auth = UserPasswordAuth(session = sessionId, user = userId, password = oldPassword), + newPassword = newPassword + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt new file mode 100644 index 0000000000..11c628e69a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.auth.password + +import dagger.Lazy +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +import im.vector.matrix.android.api.auth.password.PasswordWizard +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.auth.AuthAPI +import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams +import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.network.RetrofitFactory +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.launchToCallback +import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import kotlinx.coroutines.GlobalScope +import okhttp3.OkHttpClient +import timber.log.Timber + +internal class DefaultPasswordWizard( + okHttpClient: Lazy, + retrofitFactory: RetrofitFactory, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val homeServerConnectionConfig: HomeServerConnectionConfig +) : PasswordWizard { + + private val authAPI = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString()) + .create(AuthAPI::class.java) + + override fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback): Cancelable { + return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { + updatePasswordInternal(sessionId, userId, oldPassword, newPassword) + } + } + + private suspend fun updatePasswordInternal(sessionId: String, userId: String, oldPassword: String, newPassword: String) { + val params = UpdatePasswordParams.create(sessionId, userId, oldPassword, newPassword) + try { + executeRequest(null) { + apiCall = authAPI.updatePassword(params) + } + } catch (throwable: Throwable) { + if (throwable is Failure.OtherServerError + && throwable.httpCode == 401 + /* Avoid infinite loop */ + && params.auth?.session == null) { + try { + MoshiProvider.providesMoshi() + .adapter(RegistrationFlowResponse::class.java) + .fromJson(throwable.errorBody) + } catch (e: Exception) { + null + }?.let { + // Retry with authentication + try { + executeRequest(null) { + apiCall = authAPI.updatePassword( + params.copy(auth = params.auth?.copy(session = it.session)) + ) + } + return + } catch (failure: Throwable) { + throw failure + } + } + } + throw throwable + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UserPasswordAuth.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UserPasswordAuth.kt index 45ad43a0ef..5e672d4f59 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UserPasswordAuth.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UserPasswordAuth.kt @@ -21,7 +21,7 @@ import com.squareup.moshi.JsonClass import im.vector.matrix.android.internal.auth.data.LoginFlowTypes /** - * This class provides the authentication data to delete a device + * This class provides the authentication data by using user and password */ @JsonClass(generateAdapter = true) data class UserPasswordAuth( diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index c68972cdd4..cd2f59bc1f 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -74,6 +74,7 @@ import im.vector.riotx.features.roomprofile.RoomProfileFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment +import im.vector.riotx.features.settings.VectorSettingsGeneralFragment import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment import im.vector.riotx.features.settings.VectorSettingsLabsFragment import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment @@ -280,6 +281,11 @@ interface FragmentModule { @FragmentKey(VectorSettingsDevicesFragment::class) fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment + @Binds + @IntoMap + @FragmentKey(VectorSettingsGeneralFragment::class) + fun bindVectorSettingsGeneralFragment(fragment: VectorSettingsGeneralFragment): Fragment + @Binds @IntoMap @FragmentKey(PublicRoomsFragment::class) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 0a670e2c5a..f7a2d4735a 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -35,6 +35,9 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.cache.DiskCache import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.auth.AuthenticationService +import im.vector.matrix.android.api.failure.isInvalidPassword import im.vector.riotx.R import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.showPassword @@ -56,8 +59,11 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import javax.inject.Inject -class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { +class VectorSettingsGeneralFragment @Inject constructor( + private val authenticationService: AuthenticationService +) : VectorSettingsBaseFragment() { override var titleRes = R.string.settings_general_title override val preferenceXmlRes = R.xml.vector_settings_general @@ -109,8 +115,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { // Password mPasswordPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - notImplemented() - // onPasswordUpdateClick() + onPasswordUpdateClick() false } @@ -771,7 +776,26 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { val oldPwd = oldPasswordText.text.toString().trim() val newPwd = newPasswordText.text.toString().trim() - notImplemented() + authenticationService.getLastAuthenticatedSession()?.let { + showPasswordLoadingView(true) + authenticationService.getPasswordWizard().updatePassword(it.sessionId, it.myUserId, oldPwd, newPwd, object : MatrixCallback { + override fun onSuccess(data: Unit) { + showPasswordLoadingView(false) + dialog.dismiss() + activity.toast(R.string.settings_password_updated) + } + + override fun onFailure(failure: Throwable) { + showPasswordLoadingView(false) + if (failure.isInvalidPassword()) { + activity.toast(R.string.settings_fail_to_update_password_invalid_current_password) + } else { + activity.toast(R.string.settings_fail_to_update_password) + } + } + }) + } + /* TODO showPasswordLoadingView(true) From dfc8e8ec4c7c6b3e370cf4252182350f3ea7f9c7 Mon Sep 17 00:00:00 2001 From: onurays Date: Wed, 11 Mar 2020 23:36:04 +0300 Subject: [PATCH 03/16] AccountService is created. --- .../android/api/auth/AuthenticationService.kt | 6 -- .../matrix/android/api/session/Session.kt | 4 +- .../account/AccountService.kt} | 13 ++- .../account/model/ChangePasswordParams.kt} | 10 +-- .../matrix/android/internal/auth/AuthAPI.kt | 7 -- .../auth/DefaultAuthenticationService.kt | 19 ---- .../auth/password/DefaultPasswordWizard.kt | 87 ------------------- .../internal/session/DefaultSession.kt | 5 +- .../internal/session/SessionComponent.kt | 38 ++++---- .../internal/session/account/AccountAPI.kt | 35 ++++++++ .../internal/session/account/AccountModule.kt | 44 ++++++++++ .../session/account/ChangePasswordTask.kt | 76 ++++++++++++++++ .../session/account/DefaultAccountService.kt | 36 ++++++++ .../im/vector/riotx/core/di/FragmentModule.kt | 6 -- .../settings/VectorSettingsGeneralFragment.kt | 66 +++----------- 15 files changed, 243 insertions(+), 209 deletions(-) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/{auth/password/PasswordWizard.kt => session/account/AccountService.kt} (63%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/{internal/auth/data/UpdatePasswordParams.kt => api/session/account/model/ChangePasswordParams.kt} (75%) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountModule.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/ChangePasswordTask.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/DefaultAccountService.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt index 04e53e79f1..140d1c259f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/AuthenticationService.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.LoginFlowResult import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.login.LoginWizard -import im.vector.matrix.android.api.auth.password.PasswordWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.util.Cancelable @@ -90,9 +89,4 @@ interface AuthenticationService { fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, credentials: Credentials, callback: MatrixCallback): Cancelable - - /** - * Return a PasswordWizard, to update password. - */ - fun getPasswordWizard(): PasswordWizard } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index c2fa7d2d32..1afeed922f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.failure.GlobalError import im.vector.matrix.android.api.pushrules.PushRuleService +import im.vector.matrix.android.api.session.account.AccountService import im.vector.matrix.android.api.session.accountdata.AccountDataService import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.api.session.content.ContentUploadStateTracker @@ -59,7 +60,8 @@ interface Session : InitialSyncProgressService, HomeServerCapabilitiesService, SecureStorageService, - AccountDataService { + AccountDataService, + AccountService { /** * The params associated to the session diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/AccountService.kt similarity index 63% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/AccountService.kt index cd41d79b0a..68643ff723 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/password/PasswordWizard.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/AccountService.kt @@ -14,15 +14,20 @@ * limitations under the License. */ -package im.vector.matrix.android.api.auth.password +package im.vector.matrix.android.api.session.account import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable -interface PasswordWizard { +/** + * This interface defines methods to manage the account. It's implemented at the session level. + */ +interface AccountService { /** - * Ask the homeserver to update the password with the provided new password. + * Ask the homeserver to change the password. + * @param password Current password. + * @param newPassword New password */ - fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback): Cancelable + fun changePassword(password: String, newPassword: String, callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/model/ChangePasswordParams.kt similarity index 75% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/model/ChangePasswordParams.kt index 250bd9ec2e..83ce8eb0aa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/data/UpdatePasswordParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/account/model/ChangePasswordParams.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.matrix.android.internal.auth.data +package im.vector.matrix.android.api.session.account.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth * Class to pass request parameters to update the password. */ @JsonClass(generateAdapter = true) -internal data class UpdatePasswordParams( +internal data class ChangePasswordParams( @Json(name = "auth") val auth: UserPasswordAuth? = null, @@ -32,9 +32,9 @@ internal data class UpdatePasswordParams( val newPassword: String? = null ) { companion object { - fun create(sessionId: String, userId: String, oldPassword: String, newPassword: String): UpdatePasswordParams { - return UpdatePasswordParams( - auth = UserPasswordAuth(session = sessionId, user = userId, password = oldPassword), + fun create(userId: String, oldPassword: String, newPassword: String): ChangePasswordParams { + return ChangePasswordParams( + auth = UserPasswordAuth(user = userId, password = oldPassword), newPassword = newPassword ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt index f4258acf54..2f03c99421 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/AuthAPI.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.RiotConfig import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed -import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse import im.vector.matrix.android.internal.auth.registration.RegistrationParams @@ -103,10 +102,4 @@ internal interface AuthAPI { */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") fun resetPasswordMailConfirmed(@Body params: ResetPasswordMailConfirmed): Call - - /** - * Ask the homeserver to update the password with the provided new password. - */ - @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") - fun updatePassword(@Body params: UpdatePasswordParams): Call } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt index 1e869dd1d8..85c2cdbf3d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticationService.kt @@ -28,7 +28,6 @@ import im.vector.matrix.android.api.auth.data.Versions import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk import im.vector.matrix.android.api.auth.data.isSupportedBySdk import im.vector.matrix.android.api.auth.login.LoginWizard -import im.vector.matrix.android.api.auth.password.PasswordWizard import im.vector.matrix.android.api.auth.registration.RegistrationWizard import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.Session @@ -38,7 +37,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse import im.vector.matrix.android.internal.auth.data.RiotConfig import im.vector.matrix.android.internal.auth.db.PendingSessionData import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard -import im.vector.matrix.android.internal.auth.password.DefaultPasswordWizard import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.network.RetrofitFactory @@ -68,7 +66,6 @@ internal class DefaultAuthenticationService @Inject constructor( private var currentLoginWizard: LoginWizard? = null private var currentRegistrationWizard: RegistrationWizard? = null - private var currentPasswordWizard: PasswordWizard? = null override fun hasAuthenticatedSessions(): Boolean { return sessionParamsStore.getLast() != null @@ -224,22 +221,6 @@ internal class DefaultAuthenticationService @Inject constructor( } } - override fun getPasswordWizard(): PasswordWizard { - return currentPasswordWizard - ?: let { - sessionParamsStore.getLast()?.homeServerConnectionConfig?.let { - DefaultPasswordWizard( - okHttpClient, - retrofitFactory, - coroutineDispatchers, - it - ).also { - currentPasswordWizard = it - } - } ?: error("HomeServerConnectionConfig is null") - } - } - override fun cancelPendingLoginOrRegistration() { currentLoginWizard = null currentRegistrationWizard = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt deleted file mode 100644 index 11c628e69a..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/password/DefaultPasswordWizard.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2020 New Vector Ltd - * - * 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 im.vector.matrix.android.internal.auth.password - -import dagger.Lazy -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig -import im.vector.matrix.android.api.auth.password.PasswordWizard -import im.vector.matrix.android.api.failure.Failure -import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.auth.AuthAPI -import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams -import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse -import im.vector.matrix.android.internal.di.MoshiProvider -import im.vector.matrix.android.internal.network.RetrofitFactory -import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.task.launchToCallback -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import okhttp3.OkHttpClient -import timber.log.Timber - -internal class DefaultPasswordWizard( - okHttpClient: Lazy, - retrofitFactory: RetrofitFactory, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val homeServerConnectionConfig: HomeServerConnectionConfig -) : PasswordWizard { - - private val authAPI = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString()) - .create(AuthAPI::class.java) - - override fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback): Cancelable { - return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) { - updatePasswordInternal(sessionId, userId, oldPassword, newPassword) - } - } - - private suspend fun updatePasswordInternal(sessionId: String, userId: String, oldPassword: String, newPassword: String) { - val params = UpdatePasswordParams.create(sessionId, userId, oldPassword, newPassword) - try { - executeRequest(null) { - apiCall = authAPI.updatePassword(params) - } - } catch (throwable: Throwable) { - if (throwable is Failure.OtherServerError - && throwable.httpCode == 401 - /* Avoid infinite loop */ - && params.auth?.session == null) { - try { - MoshiProvider.providesMoshi() - .adapter(RegistrationFlowResponse::class.java) - .fromJson(throwable.errorBody) - } catch (e: Exception) { - null - }?.let { - // Retry with authentication - try { - executeRequest(null) { - apiCall = authAPI.updatePassword( - params.copy(auth = params.auth?.copy(session = it.session)) - ) - } - return - } catch (failure: Throwable) { - throw failure - } - } - } - throw throwable - } - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 25dc939196..b30c29a719 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -25,6 +25,7 @@ import im.vector.matrix.android.api.failure.GlobalError import im.vector.matrix.android.api.pushrules.PushRuleService import im.vector.matrix.android.api.session.InitialSyncProgressService import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.account.AccountService import im.vector.matrix.android.api.session.accountdata.AccountDataService import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.api.session.content.ContentUploadStateTracker @@ -94,6 +95,7 @@ internal class DefaultSession @Inject constructor( private val homeServerCapabilitiesService: Lazy, private val accountDataService: Lazy, private val _sharedSecretStorageService: Lazy, + private val accountService: Lazy, private val timelineEventDecryptor: TimelineEventDecryptor, private val shieldTrustUpdater: ShieldTrustUpdater) : Session, @@ -110,7 +112,8 @@ internal class DefaultSession @Inject constructor( SecureStorageService by secureStorageService.get(), HomeServerCapabilitiesService by homeServerCapabilitiesService.get(), ProfileService by profileService.get(), - AccountDataService by accountDataService.get() { + AccountDataService by accountDataService.get(), + AccountService by accountService.get() { override val sharedSecretStorageService: SharedSecretStorageService get() = _sharedSecretStorageService.get() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt index 22ebb4273a..b20235448a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt @@ -28,6 +28,7 @@ import im.vector.matrix.android.internal.crypto.verification.SendVerificationMes import im.vector.matrix.android.internal.di.MatrixComponent import im.vector.matrix.android.internal.di.SessionAssistedInjectModule import im.vector.matrix.android.internal.network.NetworkConnectivityChecker +import im.vector.matrix.android.internal.session.account.AccountModule import im.vector.matrix.android.internal.session.cache.CacheModule import im.vector.matrix.android.internal.session.content.ContentModule import im.vector.matrix.android.internal.session.content.UploadContentWorker @@ -55,24 +56,25 @@ import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers @Component(dependencies = [MatrixComponent::class], - modules = [ - SessionModule::class, - RoomModule::class, - SyncModule::class, - HomeServerCapabilitiesModule::class, - SignOutModule::class, - GroupModule::class, - UserModule::class, - FilterModule::class, - GroupModule::class, - ContentModule::class, - CacheModule::class, - CryptoModule::class, - PushersModule::class, - AccountDataModule::class, - ProfileModule::class, - SessionAssistedInjectModule::class - ] + modules = [ + SessionModule::class, + RoomModule::class, + SyncModule::class, + HomeServerCapabilitiesModule::class, + SignOutModule::class, + GroupModule::class, + UserModule::class, + FilterModule::class, + GroupModule::class, + ContentModule::class, + CacheModule::class, + CryptoModule::class, + PushersModule::class, + AccountDataModule::class, + ProfileModule::class, + SessionAssistedInjectModule::class, + AccountModule::class + ] ) @SessionScope internal interface SessionComponent { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt new file mode 100644 index 0000000000..6689c1a2a3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.session.account + +import im.vector.matrix.android.api.session.account.model.ChangePasswordParams +import im.vector.matrix.android.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.Headers +import retrofit2.http.POST + +internal interface AccountAPI { + + /** + * Ask the homeserver to change the password with the provided new password. + * @param params parameters to change password. + */ + @Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000") + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") + fun changePassword(@Body params: ChangePasswordParams): Call +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountModule.kt new file mode 100644 index 0000000000..87e003b0d3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountModule.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.session.account + +import dagger.Binds +import dagger.Module +import dagger.Provides +import im.vector.matrix.android.api.session.account.AccountService +import im.vector.matrix.android.internal.session.SessionScope +import retrofit2.Retrofit + +@Module +internal abstract class AccountModule { + + @Module + companion object { + @Provides + @JvmStatic + @SessionScope + fun providesAccountAPI(retrofit: Retrofit): AccountAPI { + return retrofit.create(AccountAPI::class.java) + } + } + + @Binds + abstract fun bindChangePasswordTask(task: DefaultChangePasswordTask): ChangePasswordTask + + @Binds + abstract fun bindAccountService(service: DefaultAccountService): AccountService +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/ChangePasswordTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/ChangePasswordTask.kt new file mode 100644 index 0000000000..ecd4b309d8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/ChangePasswordTask.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.session.account + +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.session.account.model.ChangePasswordParams +import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.di.UserId +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.task.Task +import org.greenrobot.eventbus.EventBus +import javax.inject.Inject + +internal interface ChangePasswordTask : Task { + data class Params( + val password: String, + val newPassword: String + ) +} + +internal class DefaultChangePasswordTask @Inject constructor( + private val accountAPI: AccountAPI, + private val eventBus: EventBus, + @UserId private val userId: String +) : ChangePasswordTask { + + override suspend fun execute(params: ChangePasswordTask.Params) { + val changePasswordParams = ChangePasswordParams.create(userId, params.password, params.newPassword) + try { + executeRequest(eventBus) { + apiCall = accountAPI.changePassword(changePasswordParams) + } + } catch (throwable: Throwable) { + if (throwable is Failure.OtherServerError + && throwable.httpCode == 401 + /* Avoid infinite loop */ + && changePasswordParams.auth?.session == null) { + try { + MoshiProvider.providesMoshi() + .adapter(RegistrationFlowResponse::class.java) + .fromJson(throwable.errorBody) + } catch (e: Exception) { + null + }?.let { + // Retry with authentication + try { + executeRequest(eventBus) { + apiCall = accountAPI.changePassword( + changePasswordParams.copy(auth = changePasswordParams.auth?.copy(session = it.session)) + ) + } + return + } catch (failure: Throwable) { + throw failure + } + } + } + throw throwable + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/DefaultAccountService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/DefaultAccountService.kt new file mode 100644 index 0000000000..fce01994d3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/DefaultAccountService.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.session.account + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.account.AccountService +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import javax.inject.Inject + +internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask, + private val taskExecutor: TaskExecutor) : AccountService { + + override fun changePassword(password: String, newPassword: String, callback: MatrixCallback): Cancelable { + return changePasswordTask + .configureWith(ChangePasswordTask.Params(password, newPassword)) { + this.callback = callback + } + .executeBy(taskExecutor) + } +} diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index cd2f59bc1f..c68972cdd4 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -74,7 +74,6 @@ import im.vector.riotx.features.roomprofile.RoomProfileFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment -import im.vector.riotx.features.settings.VectorSettingsGeneralFragment import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment import im.vector.riotx.features.settings.VectorSettingsLabsFragment import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment @@ -281,11 +280,6 @@ interface FragmentModule { @FragmentKey(VectorSettingsDevicesFragment::class) fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment - @Binds - @IntoMap - @FragmentKey(VectorSettingsGeneralFragment::class) - fun bindVectorSettingsGeneralFragment(fragment: VectorSettingsGeneralFragment): Fragment - @Binds @IntoMap @FragmentKey(PublicRoomsFragment::class) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index f7a2d4735a..68b341307a 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -36,7 +36,6 @@ import com.bumptech.glide.load.engine.cache.DiskCache import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.auth.AuthenticationService import im.vector.matrix.android.api.failure.isInvalidPassword import im.vector.riotx.R import im.vector.riotx.core.extensions.hideKeyboard @@ -59,11 +58,8 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import javax.inject.Inject -class VectorSettingsGeneralFragment @Inject constructor( - private val authenticationService: AuthenticationService -) : VectorSettingsBaseFragment() { +class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { override var titleRes = R.string.settings_general_title override val preferenceXmlRes = R.xml.vector_settings_general @@ -776,63 +772,23 @@ class VectorSettingsGeneralFragment @Inject constructor( val oldPwd = oldPasswordText.text.toString().trim() val newPwd = newPasswordText.text.toString().trim() - authenticationService.getLastAuthenticatedSession()?.let { - showPasswordLoadingView(true) - authenticationService.getPasswordWizard().updatePassword(it.sessionId, it.myUserId, oldPwd, newPwd, object : MatrixCallback { - override fun onSuccess(data: Unit) { - showPasswordLoadingView(false) - dialog.dismiss() - activity.toast(R.string.settings_password_updated) - } - - override fun onFailure(failure: Throwable) { - showPasswordLoadingView(false) - if (failure.isInvalidPassword()) { - activity.toast(R.string.settings_fail_to_update_password_invalid_current_password) - } else { - activity.toast(R.string.settings_fail_to_update_password) - } - } - }) - } - - /* TODO showPasswordLoadingView(true) - - session.updatePassword(oldPwd, newPwd, object : MatrixCallback { - private fun onDone(@StringRes textResId: Int) { + session.changePassword(oldPwd, newPwd, object : MatrixCallback { + override fun onSuccess(data: Unit) { showPasswordLoadingView(false) + dialog.dismiss() + activity.toast(R.string.settings_password_updated) + } - if (textResId == R.string.settings_fail_to_update_password_invalid_current_password) { - oldPasswordTil.error = getString(textResId) + override fun onFailure(failure: Throwable) { + showPasswordLoadingView(false) + if (failure.isInvalidPassword()) { + activity.toast(R.string.settings_fail_to_update_password_invalid_current_password) } else { - dialog.dismiss() - activity.toast(textResId, Toast.LENGTH_LONG) + activity.toast(R.string.settings_fail_to_update_password) } } - - override fun onSuccess(info: Void?) { - onDone(R.string.settings_password_updated) - } - - override fun onNetworkError(e: Exception) { - onDone(R.string.settings_fail_to_update_password) - } - - override fun onMatrixError(e: MatrixError) { - if (e.error == "Invalid password") { - onDone(R.string.settings_fail_to_update_password_invalid_current_password) - } else { - dialog.dismiss() - onDone(R.string.settings_fail_to_update_password) - } - } - - override fun onUnexpectedError(e: Exception) { - onDone(R.string.settings_fail_to_update_password) - } }) - */ } } dialog.show() From dbabe0232fbc6ff26b7a6c9d64af94693bd16b32 Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 13 Mar 2020 15:01:40 +0300 Subject: [PATCH 04/16] Do not override the default timeout. --- .../vector/matrix/android/internal/session/account/AccountAPI.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt index 6689c1a2a3..8acfb3abb8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/account/AccountAPI.kt @@ -29,7 +29,6 @@ internal interface AccountAPI { * Ask the homeserver to change the password with the provided new password. * @param params parameters to change password. */ - @Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000") @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password") fun changePassword(@Body params: ChangePasswordParams): Call } From 85493b7532aa58a75ce4ab73dede679663406920 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 15:36:30 +0200 Subject: [PATCH 05/16] Code review: use isInvalidPassword() extension everywhere. Also be robust if the Fragment is destroyed --- .../java/im/vector/matrix/android/api/failure/Extensions.kt | 1 + .../main/java/im/vector/riotx/core/error/ErrorFormatter.kt | 6 +++--- .../java/im/vector/riotx/features/login/LoginFragment.kt | 6 ++---- .../features/settings/VectorSettingsGeneralFragment.kt | 6 ++++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt index 260b3bb8a4..3a068af076 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/failure/Extensions.kt @@ -34,5 +34,6 @@ fun Throwable.shouldBeRetried(): Boolean { fun Throwable.isInvalidPassword(): Boolean { return this is Failure.ServerError + && error.code == MatrixError.M_FORBIDDEN && error.message == "Invalid password" } diff --git a/vector/src/main/java/im/vector/riotx/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/riotx/core/error/ErrorFormatter.kt index 88b6ef5463..557bc93bb1 100644 --- a/vector/src/main/java/im/vector/riotx/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/riotx/core/error/ErrorFormatter.kt @@ -18,6 +18,7 @@ package im.vector.riotx.core.error import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.MatrixError +import im.vector.matrix.android.api.failure.isInvalidPassword import im.vector.riotx.R import im.vector.riotx.core.resources.StringProvider import java.net.HttpURLConnection @@ -54,8 +55,7 @@ class DefaultErrorFormatter @Inject constructor( // Special case for terms and conditions stringProvider.getString(R.string.error_terms_not_accepted) } - throwable.error.code == MatrixError.M_FORBIDDEN - && throwable.error.message == "Invalid password" -> { + throwable.isInvalidPassword() -> { stringProvider.getString(R.string.auth_invalid_login_param) } throwable.error.code == MatrixError.M_USER_IN_USE -> { @@ -67,7 +67,7 @@ class DefaultErrorFormatter @Inject constructor( throwable.error.code == MatrixError.M_NOT_JSON -> { stringProvider.getString(R.string.login_error_not_json) } - throwable.error.code == MatrixError.M_THREEPID_DENIED -> { + throwable.error.code == MatrixError.M_THREEPID_DENIED -> { stringProvider.getString(R.string.login_error_threepid_denied) } throwable.error.code == MatrixError.M_LIMIT_EXCEEDED -> { diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt index 6fb746e095..57f914118b 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt @@ -28,6 +28,7 @@ import com.airbnb.mvrx.Success import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.MatrixError +import im.vector.matrix.android.api.failure.isInvalidPassword import im.vector.riotx.R import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.showPassword @@ -209,10 +210,7 @@ class LoginFragment @Inject constructor() : AbstractLoginFragment() { } else { // Trick to display the error without text. loginFieldTil.error = " " - if (error is Failure.ServerError - && error.error.code == MatrixError.M_FORBIDDEN - && error.error.message == "Invalid password" - && spaceInPassword()) { + if (error.isInvalidPassword() && spaceInPassword()) { passwordFieldTil.error = getString(R.string.auth_invalid_login_param_space_in_password) } else { passwordFieldTil.error = errorFormatter.toHumanReadable(error) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 68b341307a..87b27ce452 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -775,12 +775,18 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { showPasswordLoadingView(true) session.changePassword(oldPwd, newPwd, object : MatrixCallback { override fun onSuccess(data: Unit) { + if (!isAdded) { + return + } showPasswordLoadingView(false) dialog.dismiss() activity.toast(R.string.settings_password_updated) } override fun onFailure(failure: Throwable) { + if (!isAdded) { + return + } showPasswordLoadingView(false) if (failure.isInvalidPassword()) { activity.toast(R.string.settings_fail_to_update_password_invalid_current_password) From 1b416028b48bbd3f09282649dc0e7dba8dbf3d50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 15:47:30 +0200 Subject: [PATCH 06/16] Code review: inline the error instead of using toast --- .../riotx/features/settings/VectorSettingsGeneralFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 87b27ce452..b778fa02ef 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -789,9 +789,9 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { } showPasswordLoadingView(false) if (failure.isInvalidPassword()) { - activity.toast(R.string.settings_fail_to_update_password_invalid_current_password) + oldPasswordTil.error = getString(R.string.settings_fail_to_update_password_invalid_current_password) } else { - activity.toast(R.string.settings_fail_to_update_password) + oldPasswordTil.error = getString(R.string.settings_fail_to_update_password) } } }) From c50bc10f9249b7185680005cf70087de8a5bd5f6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 15:56:39 +0200 Subject: [PATCH 07/16] Code review: improve layout --- vector/src/main/res/layout/dialog_change_password.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/layout/dialog_change_password.xml b/vector/src/main/res/layout/dialog_change_password.xml index 925859bcd1..f6577f0b0d 100644 --- a/vector/src/main/res/layout/dialog_change_password.xml +++ b/vector/src/main/res/layout/dialog_change_password.xml @@ -39,7 +39,8 @@ android:id="@+id/change_password_old_pwd_til" style="@style/VectorTextInputLayout" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + app:errorEnabled="true"> + android:layout_height="wrap_content" + + android:layout_marginTop="4dp" + app:errorEnabled="true"> Date: Wed, 15 Apr 2020 16:17:58 +0200 Subject: [PATCH 08/16] Change password: update preference summary. Some people think we know the password. Using Riot-web wording --- vector/src/main/res/values/strings_riotX.xml | 3 +++ vector/src/main/res/xml/vector_settings_general.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 00bf65e121..286cd42af4 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -29,4 +29,7 @@ + + Set a new account password… + diff --git a/vector/src/main/res/xml/vector_settings_general.xml b/vector/src/main/res/xml/vector_settings_general.xml index c49eca825a..ac8fb8445c 100644 --- a/vector/src/main/res/xml/vector_settings_general.xml +++ b/vector/src/main/res/xml/vector_settings_general.xml @@ -19,7 +19,7 @@ From 9fe32fe915ff5005783c4740869c5108dcb91ce1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 16:19:53 +0200 Subject: [PATCH 09/16] Change password: hide the preference (as Riot-Web does) if it is not possible to change the password. --- .../homeserver/HomeServerCapabilities.kt | 4 ++ .../mapper/HomeServerCapabilitiesMapper.kt | 2 + .../model/HomeServerCapabilitiesEntity.kt | 1 + .../session/homeserver/CapabilitiesAPI.kt | 6 ++ .../DefaultGetHomeServerCapabilitiesTask.kt | 12 +++- .../homeserver/GetCapabilitiesResult.kt | 58 +++++++++++++++++++ .../homeserver/GetUploadCapabilitiesResult.kt | 2 +- .../settings/VectorSettingsGeneralFragment.kt | 11 +++- 8 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetCapabilitiesResult.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/homeserver/HomeServerCapabilities.kt index 9b01cdef3b..c8526985e1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/homeserver/HomeServerCapabilities.kt @@ -17,6 +17,10 @@ package im.vector.matrix.android.api.session.homeserver data class HomeServerCapabilities( + /** + * True if it is possible to change the password of the account. + */ + val canChangePassword: Boolean = true, /** * Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/HomeServerCapabilitiesMapper.kt index 66b0f1b379..a0d3662e03 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/HomeServerCapabilitiesMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/HomeServerCapabilitiesMapper.kt @@ -26,12 +26,14 @@ internal object HomeServerCapabilitiesMapper { fun map(entity: HomeServerCapabilitiesEntity): HomeServerCapabilities { return HomeServerCapabilities( + canChangePassword = entity.canChangePassword, maxUploadFileSize = entity.maxUploadFileSize ) } fun map(domain: HomeServerCapabilities): HomeServerCapabilitiesEntity { return HomeServerCapabilitiesEntity( + canChangePassword = domain.canChangePassword, maxUploadFileSize = domain.maxUploadFileSize ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/HomeServerCapabilitiesEntity.kt index 0d067286e1..5743597a61 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/HomeServerCapabilitiesEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/HomeServerCapabilitiesEntity.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities import io.realm.RealmObject internal open class HomeServerCapabilitiesEntity( + var canChangePassword: Boolean = true, var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN, var lastUpdatedTimestamp: Long = 0L ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/CapabilitiesAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/CapabilitiesAPI.kt index c83a61cfb0..f37bbfe798 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/CapabilitiesAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/CapabilitiesAPI.kt @@ -22,6 +22,12 @@ import retrofit2.http.GET internal interface CapabilitiesAPI { + /** + * Request the homeserver capabilities + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "capabilities") + fun getCapabilities(): Call + /** * Request the upload capabilities */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt index 2dbd627ce5..9f068381f0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt @@ -51,15 +51,23 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( apiCall = capabilitiesAPI.getUploadCapabilities() } + val capabilities = runCatching { + executeRequest(eventBus) { + apiCall = capabilitiesAPI.getCapabilities() + } + }.getOrNull() + // TODO Add other call here (get version, etc.) - insertInDb(uploadCapabilities) + insertInDb(capabilities, uploadCapabilities) } - private suspend fun insertInDb(getUploadCapabilitiesResult: GetUploadCapabilitiesResult) { + private suspend fun insertInDb(getCapabilitiesResult: GetCapabilitiesResult?, getUploadCapabilitiesResult: GetUploadCapabilitiesResult) { monarchy.awaitTransaction { realm -> val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) + homeServerCapabilitiesEntity.canChangePassword = getCapabilitiesResult.canChangePassword() + homeServerCapabilitiesEntity.maxUploadFileSize = getUploadCapabilitiesResult.maxUploadSize ?: HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetCapabilitiesResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetCapabilitiesResult.kt new file mode 100644 index 0000000000..ffaa998789 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetCapabilitiesResult.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.internal.session.homeserver + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.extensions.orTrue + +/** + * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-capabilities + */ +@JsonClass(generateAdapter = true) +internal data class GetCapabilitiesResult( + /** + * Required. The custom capabilities the server supports, using the Java package naming convention. + */ + @Json(name = "capabilities") + val capabilities: Capabilities? = null +) + +@JsonClass(generateAdapter = true) +internal data class Capabilities( + /** + * Capability to indicate if the user can change their password. + */ + @Json(name = "m.change_password") + val changePassword: ChangePassword? = null + + // No need for m.room_versions for the moment +) + +@JsonClass(generateAdapter = true) +internal data class ChangePassword( + /** + * Required. True if the user can change their password, false otherwise. + */ + @Json(name = "enabled") + val enabled: Boolean? +) + +// The spec says: If not present, the client should assume that password changes are possible via the API +internal fun GetCapabilitiesResult?.canChangePassword(): Boolean { + return this?.capabilities?.changePassword?.enabled.orTrue() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetUploadCapabilitiesResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetUploadCapabilitiesResult.kt index 8e410cc834..c98119db6c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetUploadCapabilitiesResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/homeserver/GetUploadCapabilitiesResult.kt @@ -20,7 +20,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class GetUploadCapabilitiesResult( +internal data class GetUploadCapabilitiesResult( /** * The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content. * If not listed or null, the size limit should be treated as unknown. diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index b778fa02ef..742bbc8201 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -110,9 +110,14 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { } // Password - mPasswordPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - onPasswordUpdateClick() - false + // Hide the preference if password can not be updated + if (session.getHomeServerCapabilities().canChangePassword) { + mPasswordPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { + onPasswordUpdateClick() + false + } + } else { + mPasswordPreference.isVisible = false } // Add Email From d44e43d94b3657e2a89c03e338cf260cda62498e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 16:29:14 +0200 Subject: [PATCH 10/16] Change password: update wording of button (Nad's request) --- .../riotx/features/settings/VectorSettingsGeneralFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 742bbc8201..1a95f23ff7 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -704,7 +704,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { val dialog = AlertDialog.Builder(activity) .setView(view) - .setPositiveButton(R.string.settings_change_password_submit, null) + .setPositiveButton(R.string.settings_change_password, null) .setNegativeButton(R.string.cancel, null) .setOnDismissListener { view.hideKeyboard() From 7bf1f916c40e0f9987ff2e74a16b29dc1b9da99f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 16:30:05 +0200 Subject: [PATCH 11/16] Change password: lambda --- .../settings/VectorSettingsGeneralFragment.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 1a95f23ff7..6ad04dfeb2 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -690,17 +690,15 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { var passwordShown = false - showPassword.setOnClickListener(object : View.OnClickListener { - override fun onClick(v: View?) { - passwordShown = !passwordShown + showPassword.setOnClickListener { + passwordShown = !passwordShown - oldPasswordText.showPassword(passwordShown) - newPasswordText.showPassword(passwordShown) - confirmNewPasswordText.showPassword(passwordShown) + oldPasswordText.showPassword(passwordShown) + newPasswordText.showPassword(passwordShown) + confirmNewPasswordText.showPassword(passwordShown) - showPassword.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) - } - }) + showPassword.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) + } val dialog = AlertDialog.Builder(activity) .setView(view) From 5666965321d8c657fade889e6f379ec2207baed0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 16:32:08 +0200 Subject: [PATCH 12/16] Change password: do not trim passwords --- .../features/settings/VectorSettingsGeneralFragment.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 6ad04dfeb2..dd09f6ea23 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -714,9 +714,9 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { updateButton.isEnabled = false fun updateUi() { - val oldPwd = oldPasswordText.text.toString().trim() - val newPwd = newPasswordText.text.toString().trim() - val newConfirmPwd = confirmNewPasswordText.text.toString().trim() + val oldPwd = oldPasswordText.text.toString() + val newPwd = newPasswordText.text.toString() + val newConfirmPwd = confirmNewPasswordText.text.toString() updateButton.isEnabled = oldPwd.isNotEmpty() && newPwd.isNotEmpty() && newPwd == newConfirmPwd @@ -772,8 +772,8 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { view.hideKeyboard() - val oldPwd = oldPasswordText.text.toString().trim() - val newPwd = newPasswordText.text.toString().trim() + val oldPwd = oldPasswordText.text.toString() + val newPwd = newPasswordText.text.toString() showPasswordLoadingView(true) session.changePassword(oldPwd, newPwd, object : MatrixCallback { From bf5e2b96df45b626f0d3566d2db921510221ffb5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 16:33:42 +0200 Subject: [PATCH 13/16] Change password: bigger margins --- vector/src/main/res/layout/dialog_change_password.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/layout/dialog_change_password.xml b/vector/src/main/res/layout/dialog_change_password.xml index f6577f0b0d..12806a56b7 100644 --- a/vector/src/main/res/layout/dialog_change_password.xml +++ b/vector/src/main/res/layout/dialog_change_password.xml @@ -55,8 +55,7 @@ style="@style/VectorTextInputLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - - android:layout_marginTop="4dp" + android:layout_marginTop="8dp" app:errorEnabled="true"> Date: Wed, 15 Apr 2020 16:37:45 +0200 Subject: [PATCH 14/16] Change password: prevent cancellation when processing --- .../riotx/features/settings/VectorSettingsGeneralFragment.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index dd09f6ea23..f754064fbc 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -702,6 +702,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { val dialog = AlertDialog.Builder(activity) .setView(view) + .setCancelable(false) .setPositiveButton(R.string.settings_change_password, null) .setNegativeButton(R.string.cancel, null) .setOnDismissListener { @@ -711,6 +712,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { dialog.setOnShowListener { val updateButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) + val cancelButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE) updateButton.isEnabled = false fun updateUi() { @@ -754,6 +756,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { confirmNewPasswordText.isEnabled = false changePasswordLoader.isVisible = true updateButton.isEnabled = false + cancelButton.isEnabled = false } else { showPassword.isEnabled = true oldPasswordText.isEnabled = true @@ -761,6 +764,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { confirmNewPasswordText.isEnabled = true changePasswordLoader.isVisible = false updateButton.isEnabled = true + cancelButton.isEnabled = true } } From 17ece54cb0973d35e1e521eac3b6af9f9a58ea94 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Apr 2020 10:16:10 +0200 Subject: [PATCH 15/16] `mimetype` field is optional --- CHANGES.md | 1 + .../android/api/session/room/model/message/AudioInfo.kt | 2 +- .../android/api/session/room/model/message/ThumbnailInfo.kt | 2 +- .../internal/session/room/send/LocalEchoEventFactory.kt | 5 ++--- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4ca393ab73..1ed4d31224 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,7 @@ Bugfix 🐛: - Cross-Signing | web <-> riotX After QR code scan, gossiping fails (#1210) - Fix crash when trying to download file without internet connection (#1229) - Local echo are not updated in timeline (for failed & encrypted states) + - Render image event even if thumbnail_info does not have mimetype defined (#1209) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/AudioInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/AudioInfo.kt index eef6b283c6..877746705f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/AudioInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/AudioInfo.kt @@ -24,7 +24,7 @@ data class AudioInfo( /** * The mimetype of the audio e.g. "audio/aac". */ - @Json(name = "mimetype") val mimeType: String, + @Json(name = "mimetype") val mimeType: String?, /** * The size of the audio clip in bytes. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/ThumbnailInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/ThumbnailInfo.kt index 0ef1b8f658..0ee6b88e01 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/ThumbnailInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/ThumbnailInfo.kt @@ -39,5 +39,5 @@ data class ThumbnailInfo( /** * The mimetype of the image, e.g. "image/jpeg". */ - @Json(name = "mimetype") val mimeType: String + @Json(name = "mimetype") val mimeType: String? ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index a4a6eb6972..5f0515e669 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -314,7 +314,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_AUDIO, body = attachment.name ?: "audio", audioInfo = AudioInfo( - mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() } ?: "audio/mpeg", + mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() }, size = attachment.size ), url = attachment.queryUri.toString() @@ -327,8 +327,7 @@ internal class LocalEchoEventFactory @Inject constructor( msgType = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() } - ?: "application/octet-stream", + mimeType = attachment.getSafeMimeType()?.takeIf { it.isNotBlank() }, size = attachment.size ), url = attachment.queryUri.toString() From d74a5f9979f33c6ed6a398bc25d8a94b758435b6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Apr 2020 09:37:19 +0200 Subject: [PATCH 16/16] Typo --- .../verification/qrcode/DefaultQrCodeVerificationTransaction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt index 59ee23cc62..53fecc4865 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt @@ -177,7 +177,7 @@ internal class DefaultQrCodeVerificationTransaction( }.exhaustive if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) { - // // Nothing to verify + // Nothing to verify cancel(CancelCode.MismatchedKeys) return }