From 14fc31be07ef3c672330b1777ce7e98d0a06c70c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Feb 2021 14:05:01 +0100 Subject: [PATCH 01/12] Add homeserver version check --- tools/hs_diag.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/hs_diag.py b/tools/hs_diag.py index ded10a562d..7d7a947c4c 100755 --- a/tools/hs_diag.py +++ b/tools/hs_diag.py @@ -48,7 +48,8 @@ print("Get information from " + baseUrl) items = [ # [Title, URL, True for GET request and False for POST request] ["Well-known", baseUrl + ".well-known/matrix/client", True] - , ["Version", baseUrl + "_matrix/client/versions", True] + , ["API version", baseUrl + "_matrix/client/versions", True] + , ["Homeserver version", baseUrl + "_matrix/federation/v1/version", True] , ["Login flow", baseUrl + "_matrix/client/r0/login", True] , ["Registration flow", baseUrl + "_matrix/client/r0/register", False] # Useless , ["Username availability", baseUrl + "_matrix/client/r0/register/available?username=benoit", True] From a8be5ed6b02b942e2059cdb3f3ce4e01ad26817c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Feb 2021 14:50:50 +0100 Subject: [PATCH 02/12] Create FederationModule --- .../sdk/api/federation/FederationService.kt | 24 +++++++++ .../sdk/api/federation/FederationVersion.kt | 31 ++++++++++++ .../matrix/android/sdk/api/session/Session.kt | 6 +++ .../federation/DefaultFederationService.kt | 29 +++++++++++ .../sdk/internal/federation/FederationAPI.kt | 27 ++++++++++ .../federation/FederationGetVersionResult.kt | 37 ++++++++++++++ .../internal/federation/FederationModule.kt | 49 +++++++++++++++++++ .../federation/GetFederationVersionTask.kt | 40 +++++++++++++++ .../sdk/internal/network/NetworkConstants.kt | 3 ++ .../sdk/internal/session/DefaultSession.kt | 4 ++ .../sdk/internal/session/SessionComponent.kt | 2 + 11 files changed, 252 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt new file mode 100644 index 0000000000..0761ef8d21 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationService.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 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.api.federation + +interface FederationService { + /** + * Get information about the homeserver + */ + suspend fun getFederationVersion(): FederationVersion +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt new file mode 100644 index 0000000000..2ed3f848b6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/federation/FederationVersion.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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.api.federation + +/** + * Ref: https://matrix.org/docs/spec/server_server/latest#get-matrix-federation-v1-version + */ +data class FederationVersion( + /** + * Arbitrary name that identify this implementation. + */ + val name: String?, + /** + * Version of this implementation. The version format depends on the implementation. + */ + val version: String? +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index 039025e0df..86ac0056e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.session.account.AccountService import org.matrix.android.sdk.api.session.accountdata.AccountDataService @@ -213,6 +214,11 @@ interface Session : */ fun searchService(): SearchService + /** + * Returns the federation service associated with the session + */ + fun federationService(): FederationService + /** * Returns the third party service associated with the session */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt new file mode 100644 index 0000000000..862a4855cc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/DefaultFederationService.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.api.federation.FederationService +import org.matrix.android.sdk.api.federation.FederationVersion +import javax.inject.Inject + +internal class DefaultFederationService @Inject constructor( + private val getFederationVersionTask: GetFederationVersionTask +) : FederationService { + override suspend fun getFederationVersion(): FederationVersion { + return getFederationVersionTask.execute(Unit) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt new file mode 100644 index 0000000000..1816616336 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationAPI.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.internal.network.NetworkConstants +import retrofit2.Call +import retrofit2.http.GET + +internal interface FederationAPI { + @GET(NetworkConstants.URI_FEDERATION_PATH + "version") + fun getVersion(): Call +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt new file mode 100644 index 0000000000..4e436c0da2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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.federation + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Ref: https://matrix.org/docs/spec/server_server/latest#get-matrix-federation-v1-version + */ +@JsonClass(generateAdapter = true) +internal data class FederationGetVersionResult( + /** + * Arbitrary name that identify this implementation. + */ + @Json(name = "name") + val name: String?, + /** + * Version of this implementation. The version format depends on the implementation. + */ + @Json(name = "version") + val version: String? +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt new file mode 100644 index 0000000000..320bf1d445 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationModule.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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.federation + +import dagger.Binds +import dagger.Lazy +import dagger.Module +import dagger.Provides +import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.federation.FederationService +import org.matrix.android.sdk.internal.di.Unauthenticated +import org.matrix.android.sdk.internal.network.RetrofitFactory + +@Module +internal abstract class FederationModule { + + @Module + companion object { + @Provides + @JvmStatic + fun providesFederationAPI(@Unauthenticated okHttpClient: Lazy, + sessionParams: SessionParams, + retrofitFactory: RetrofitFactory): FederationAPI { + return retrofitFactory.create(okHttpClient, sessionParams.homeServerUrl).create(FederationAPI::class.java) + } + } + + @Binds + abstract fun bindFederationService(service: DefaultFederationService): FederationService + + @Binds + abstract fun bindGetFederationVersionTask(task: DefaultGetFederationVersionTask): GetFederationVersionTask +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt new file mode 100644 index 0000000000..6000ff6d31 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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.federation + +import org.matrix.android.sdk.api.federation.FederationVersion +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetFederationVersionTask : Task + +internal class DefaultGetFederationVersionTask @Inject constructor( + private val federationAPI: FederationAPI +) : GetFederationVersionTask { + + override suspend fun execute(params: Unit): FederationVersion { + val result = executeRequest(null) { + apiCall = federationAPI.getVersion() + } + + return FederationVersion( + name = result.name, + version = result.version + ) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt index a14c86efb6..99c12255cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt @@ -36,4 +36,7 @@ internal object NetworkConstants { // Integration const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/" + + // Federation + const val URI_FEDERATION_PATH = "_matrix/federation/v1/" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index 890f3a6ac3..06bb4bd929 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -22,6 +22,7 @@ import io.realm.RealmConfiguration import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.federation.FederationService import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.session.InitialSyncProgressService import org.matrix.android.sdk.api.session.Session @@ -88,6 +89,7 @@ internal class DefaultSession @Inject constructor( private val groupService: Lazy, private val userService: Lazy, private val filterService: Lazy, + private val federationService: Lazy, private val cacheService: Lazy, private val signOutService: Lazy, private val pushRuleService: Lazy, @@ -260,6 +262,8 @@ internal class DefaultSession @Inject constructor( override fun searchService(): SearchService = searchService.get() + override fun federationService(): FederationService = federationService.get() + override fun thirdPartyService(): ThirdPartyService = thirdPartyService.get() override fun getOkHttpClient(): OkHttpClient { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 9279c5c97a..7e1e3d0f70 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.internal.crypto.SendGossipWorker import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker import org.matrix.android.sdk.internal.di.MatrixComponent +import org.matrix.android.sdk.internal.federation.FederationModule import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.session.account.AccountModule import org.matrix.android.sdk.internal.session.cache.CacheModule @@ -87,6 +88,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers AccountDataModule::class, ProfileModule::class, AccountModule::class, + FederationModule::class, CallModule::class, SearchModule::class, ThirdPartyModule::class From 531beb0a86d6f923b83ce7e0e8098a80d26c0534 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Feb 2021 17:01:35 +0100 Subject: [PATCH 03/12] Add a screen to view details about the homeserver --- .../federation/FederationGetVersionResult.kt | 6 ++ .../federation/GetFederationVersionTask.kt | 4 +- .../im/vector/app/core/di/FragmentModule.kt | 6 ++ .../discovery/SettingsCenteredImageItem.kt | 41 ++++++++ .../homeserver/HomeServerSettingsViewState.kt | 27 +++++ .../homeserver/HomeserverSettingsAction.kt | 23 +++++ .../HomeserverSettingsController.kt | 98 +++++++++++++++++++ .../homeserver/HomeserverSettingsFragment.kt | 73 ++++++++++++++ .../homeserver/HomeserverSettingsViewModel.kt | 92 +++++++++++++++++ vector/src/main/res/drawable/ic_layers.xml | 27 +++++ .../layout/item_settings_centered_image.xml | 11 +++ vector/src/main/res/values/strings.xml | 3 + .../main/res/xml/vector_settings_general.xml | 1 + 13 files changed, 410 insertions(+), 2 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/discovery/SettingsCenteredImageItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt create mode 100644 vector/src/main/res/drawable/ic_layers.xml create mode 100644 vector/src/main/res/layout/item_settings_centered_image.xml diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt index 4e436c0da2..3d84008be6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/FederationGetVersionResult.kt @@ -24,6 +24,12 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class FederationGetVersionResult( + @Json(name = "server") + val server: FederationGetVersionServer? +) + +@JsonClass(generateAdapter = true) +internal data class FederationGetVersionServer( /** * Arbitrary name that identify this implementation. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt index 6000ff6d31..ce35e48f6b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/federation/GetFederationVersionTask.kt @@ -33,8 +33,8 @@ internal class DefaultGetFederationVersionTask @Inject constructor( } return FederationVersion( - name = result.name, - version = result.version + name = result.server?.name, + version = result.server?.version ) } } diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index eff484fb9a..430aee5468 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -109,6 +109,7 @@ import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailFragmen import im.vector.app.features.settings.devtools.IncomingKeyRequestListFragment import im.vector.app.features.settings.devtools.KeyRequestsFragment import im.vector.app.features.settings.devtools.OutgoingKeyRequestListFragment +import im.vector.app.features.settings.homeserver.HomeserverSettingsFragment import im.vector.app.features.settings.ignored.VectorSettingsIgnoredUsersFragment import im.vector.app.features.settings.locale.LocalePickerFragment import im.vector.app.features.settings.push.PushGatewaysFragment @@ -284,6 +285,11 @@ interface FragmentModule { @FragmentKey(VectorSettingsLabsFragment::class) fun bindVectorSettingsLabsFragment(fragment: VectorSettingsLabsFragment): Fragment + @Binds + @IntoMap + @FragmentKey(HomeserverSettingsFragment::class) + fun bindHomeserverSettingsFragment(fragment: HomeserverSettingsFragment): Fragment + @Binds @IntoMap @FragmentKey(VectorSettingsPinFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/discovery/SettingsCenteredImageItem.kt b/vector/src/main/java/im/vector/app/features/discovery/SettingsCenteredImageItem.kt new file mode 100644 index 0000000000..af79dd8bb5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/discovery/SettingsCenteredImageItem.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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.app.features.discovery + +import android.widget.ImageView +import androidx.annotation.DrawableRes +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.airbnb.epoxy.EpoxyModelWithHolder +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder + +@EpoxyModelClass(layout = R.layout.item_settings_centered_image) +abstract class SettingsCenteredImageItem : EpoxyModelWithHolder() { + + @EpoxyAttribute + @DrawableRes + var drawableRes: Int = 0 + + override fun bind(holder: Holder) { + super.bind(holder) + holder.image.setImageResource(drawableRes) + } + + class Holder : VectorEpoxyHolder() { + val image by bind(R.id.itemSettingsImage) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt new file mode 100644 index 0000000000..7c1a78d0cf --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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.app.features.settings.homeserver + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.api.federation.FederationVersion + +data class HomeServerSettingsViewState( + val baseUrl: String = "", + val federationVersion: Async = Uninitialized +) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsAction.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsAction.kt new file mode 100644 index 0000000000..6ab097f4bf --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsAction.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 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.app.features.settings.homeserver + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class HomeserverSettingsAction : VectorViewModelAction { + object Refresh : HomeserverSettingsAction() +} diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt new file mode 100644 index 0000000000..4f648309f0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 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.app.features.settings.homeserver + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import im.vector.app.R +import im.vector.app.core.epoxy.errorWithRetryItem +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.features.discovery.settingsCenteredImageItem +import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.discovery.settingsSectionTitleItem +import org.matrix.android.sdk.api.federation.FederationVersion +import javax.inject.Inject + +class HomeserverSettingsController @Inject constructor( + private val errorFormatter: ErrorFormatter +) : TypedEpoxyController() { + + var callback: Callback? = null + + interface Callback { + fun retry() + } + + override fun buildModels(data: HomeServerSettingsViewState?) { + data ?: return + + buildHeader(data) + when (val federationVersion = data.federationVersion) { + is Loading, + is Uninitialized -> + loadingItem { + id("loading") + } + is Fail -> + errorWithRetryItem { + id("error") + text(errorFormatter.toHumanReadable(federationVersion.error)) + listener { callback?.retry() } + } + is Success -> + buildFederationVersion(federationVersion()) + } + } + + private fun buildHeader(state: HomeServerSettingsViewState) { + settingsCenteredImageItem { + id("icon") + drawableRes(R.drawable.ic_layers) + } + settingsSectionTitleItem { + id("urlTitle") + titleResId(R.string.hs_url) + } + settingsInfoItem { + id("urlValue") + helperText(state.baseUrl) + } + } + + private fun buildFederationVersion(federationVersion: FederationVersion) { + settingsSectionTitleItem { + id("nameTitle") + titleResId(R.string.settings_server_name) + } + settingsInfoItem { + id("nameValue") + helperText(federationVersion.name) + } + settingsSectionTitleItem { + id("versionTitle") + titleResId(R.string.settings_server_version) + } + settingsInfoItem { + id("versionValue") + helperText(federationVersion.version) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt new file mode 100644 index 0000000000..20541a1ebb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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.app.features.settings.homeserver + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentGenericRecyclerBinding +import javax.inject.Inject + +/** + * Display some information about the homeserver + */ +class HomeserverSettingsFragment @Inject constructor( + val homeserverSettingsViewModelFactory: HomeserverSettingsViewModel.Factory, + private val homeserverSettingsController: HomeserverSettingsController +) : VectorBaseFragment(), + HomeserverSettingsController.Callback { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { + return FragmentGenericRecyclerBinding.inflate(inflater, container, false) + } + + private val viewModel: HomeserverSettingsViewModel by fragmentViewModel() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + homeserverSettingsController.callback = this + views.genericRecyclerView.configureWith(homeserverSettingsController) + } + + override fun onDestroyView() { + homeserverSettingsController.callback = null + views.genericRecyclerView.cleanup() + super.onDestroyView() + } + + override fun onResume() { + super.onResume() + (activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.settings_home_server) + } + + override fun retry() { + viewModel.handle(HomeserverSettingsAction.Refresh) + } + + override fun invalidate() = withState(viewModel) { state -> + homeserverSettingsController.setData(state) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt new file mode 100644 index 0000000000..19578aea81 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 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.app.features.settings.homeserver + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session + +class HomeserverSettingsViewModel @AssistedInject constructor( + @Assisted initialState: HomeServerSettingsViewState, + private val session: Session +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: HomeServerSettingsViewState): HomeserverSettingsViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: HomeServerSettingsViewState): HomeserverSettingsViewModel? { + val fragment: HomeserverSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.homeserverSettingsViewModelFactory.create(state) + } + } + + init { + setState { + copy( + baseUrl = session.sessionParams.homeServerUrl + ) + } + fetchHomeserverVersion() + } + + private fun fetchHomeserverVersion() { + setState { + copy( + federationVersion = Loading() + ) + } + + viewModelScope.launch { + try { + val federationVersion = session.federationService().getFederationVersion() + setState { + copy( + federationVersion = Success(federationVersion) + ) + } + } catch (failure: Throwable) { + setState { + copy( + federationVersion = Fail(failure) + ) + } + } + } + } + + override fun handle(action: HomeserverSettingsAction) { + when (action) { + HomeserverSettingsAction.Refresh -> fetchHomeserverVersion() + } + } +} diff --git a/vector/src/main/res/drawable/ic_layers.xml b/vector/src/main/res/drawable/ic_layers.xml new file mode 100644 index 0000000000..658630add0 --- /dev/null +++ b/vector/src/main/res/drawable/ic_layers.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/vector/src/main/res/layout/item_settings_centered_image.xml b/vector/src/main/res/layout/item_settings_centered_image.xml new file mode 100644 index 0000000000..ee249448e7 --- /dev/null +++ b/vector/src/main/res/layout/item_settings_centered_image.xml @@ -0,0 +1,11 @@ + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 79f1514b2a..d71720f64a 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2336,6 +2336,9 @@ Manage Sessions Sign out of this session + Server name + Server version + No cryptographic information available This session is trusted for secure messaging because you verified it: diff --git a/vector/src/main/res/xml/vector_settings_general.xml b/vector/src/main/res/xml/vector_settings_general.xml index c1fe82e4c2..73ce09eb22 100644 --- a/vector/src/main/res/xml/vector_settings_general.xml +++ b/vector/src/main/res/xml/vector_settings_general.xml @@ -81,6 +81,7 @@ Date: Tue, 16 Feb 2021 18:52:48 +0100 Subject: [PATCH 04/12] Add server version in rageshakes --- .../features/rageshake/BugReportActivity.kt | 10 ++- .../app/features/rageshake/BugReportState.kt | 24 ++++++ .../features/rageshake/BugReportViewModel.kt | 76 +++++++++++++++++++ .../app/features/rageshake/BugReporter.kt | 2 + 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt create mode 100644 vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt index 930e41e0d9..024d84f27b 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt @@ -21,12 +21,15 @@ import android.view.MenuItem import android.widget.Toast import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged +import com.airbnb.mvrx.viewModel +import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityBugReportBinding import timber.log.Timber +import javax.inject.Inject /** * Form to send a bug report @@ -39,6 +42,10 @@ class BugReportActivity : VectorBaseActivity() { override fun getBinding() = ActivityBugReportBinding.inflate(layoutInflater) + @Inject lateinit var bugReportViewModelFactory: BugReportViewModel.Factory + + private val viewModel: BugReportViewModel by viewModel() + private var forSuggestion: Boolean = false override fun initUiAndData() { @@ -114,7 +121,7 @@ class BugReportActivity : VectorBaseActivity() { /** * Send the bug report */ - private fun sendBugReport() { + private fun sendBugReport() = withState(viewModel) { state -> views.bugReportScrollview.alpha = 0.3f views.bugReportMaskView.isVisible = true @@ -133,6 +140,7 @@ class BugReportActivity : VectorBaseActivity() { views.bugReportButtonIncludeKeyShareHistory.isChecked, views.bugReportButtonIncludeScreenshot.isChecked, views.bugReportEditText.text.toString(), + state.serverVersion, object : BugReporter.IMXBugReportListener { override fun onUploadFailed(reason: String?) { try { diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt new file mode 100644 index 0000000000..1dc316a5e2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 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.app.features.rageshake + +import com.airbnb.mvrx.MvRxState + +data class BugReportState( + val serverVersion: String = "" +) : MvRxState + diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt new file mode 100644 index 0000000000..c71a89553e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 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.app.features.rageshake + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull + +class BugReportViewModel @AssistedInject constructor( + @Assisted initialState: BugReportState, + val activeSessionHolder: ActiveSessionHolder +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: BugReportState): BugReportViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: BugReportState): BugReportViewModel? { + val activity: BugReportActivity = (viewModelContext as ActivityViewModelContext).activity() + return activity.bugReportViewModelFactory.create(state) + } + } + + init { + fetchHomeserverVersion() + } + + private fun fetchHomeserverVersion() { + viewModelScope.launch { + val version = tryOrNull { + activeSessionHolder.getSafeActiveSession() + ?.federationService() + ?.getFederationVersion() + ?.let { "${it.name} - ${it.version}" } + } ?: "undefined" + + setState { + copy( + serverVersion = version + ) + } + } + } + + override fun handle(action: EmptyAction) { + // No op + } +} diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index a43aca488d..15fd18039b 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -165,6 +165,7 @@ class BugReporter @Inject constructor( withKeyRequestHistory: Boolean, withScreenshot: Boolean, theBugDescription: String, + serverVersion: String, listener: IMXBugReportListener?) { // enumerate files to delete val mBugReportFiles: MutableList = ArrayList() @@ -273,6 +274,7 @@ class BugReporter @Inject constructor( .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) + .addFormDataPart("server_version", serverVersion) val buildNumber = context.getString(R.string.build_number) if (buildNumber.isNotEmpty() && buildNumber != "0") { From e1f778e21add98853a6ee7c19695a7ed9ca0035a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Feb 2021 18:56:49 +0100 Subject: [PATCH 05/12] Changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b67e33f9ad..cfff2756ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Features ✨: - Improvements πŸ™Œ: - - + - Fetch homeserver type and version and display in a new wetting screen and add info in rageshakes (#2831) Bugfix πŸ›: - From 80524fb8c1fee3fa5326326e0e42f1a11d455054 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 16 Feb 2021 19:34:26 +0100 Subject: [PATCH 06/12] Cleanup --- CHANGES.md | 2 +- .../java/im/vector/app/features/rageshake/BugReportState.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cfff2756ef..422d94ff06 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Features ✨: - Improvements πŸ™Œ: - - Fetch homeserver type and version and display in a new wetting screen and add info in rageshakes (#2831) + - Fetch homeserver type and version and display in a new setting screen and add info in rageshakes (#2831) Bugfix πŸ›: - diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt index 1dc316a5e2..a5019115fb 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportState.kt @@ -21,4 +21,3 @@ import com.airbnb.mvrx.MvRxState data class BugReportState( val serverVersion: String = "" ) : MvRxState - From f9f54cabdd396e6daab19c7c08e0aa920913ec8e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 18 Feb 2021 23:40:03 +0100 Subject: [PATCH 07/12] Add upload capability on the screen and give possibility to refresh the data --- .../HomeServerCapabilitiesService.kt | 5 ++++ .../DefaultHomeServerCapabilitiesService.kt | 11 ++++++-- .../GetHomeServerCapabilitiesTask.kt | 20 +++++++++----- .../sdk/internal/session/sync/SyncTask.kt | 2 +- .../homeserver/HomeServerSettingsViewState.kt | 2 ++ .../HomeserverSettingsController.kt | 26 +++++++++++++++++-- .../homeserver/HomeserverSettingsViewModel.kt | 18 ++++++++++++- vector/src/main/res/values/strings.xml | 3 +++ 8 files changed, 74 insertions(+), 13 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt index 2c9121ce4a..f12cbcd6db 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilitiesService.kt @@ -21,6 +21,11 @@ package org.matrix.android.sdk.api.session.homeserver */ interface HomeServerCapabilitiesService { + /** + * Force a refresh of the stored data + */ + suspend fun refreshHomeServerCapabilities() + /** * Get the HomeServer capabilities */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt index 27396aac80..0ed690d972 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultHomeServerCapabilitiesService.kt @@ -17,16 +17,23 @@ package org.matrix.android.sdk.internal.session.homeserver import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.internal.database.mapper.HomeServerCapabilitiesMapper import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity import org.matrix.android.sdk.internal.database.query.get import org.matrix.android.sdk.internal.di.SessionDatabase -import io.realm.Realm import javax.inject.Inject -internal class DefaultHomeServerCapabilitiesService @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : HomeServerCapabilitiesService { +internal class DefaultHomeServerCapabilitiesService @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, + private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask +) : HomeServerCapabilitiesService { + + override suspend fun refreshHomeServerCapabilities() { + getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = true)) + } override fun getHomeServerCapabilities(): HomeServerCapabilities { return Realm.getInstance(monarchy.realmConfiguration).use { realm -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 845cfb392e..84c9132d61 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -38,7 +38,11 @@ import timber.log.Timber import java.util.Date import javax.inject.Inject -internal interface GetHomeServerCapabilitiesTask : Task +internal interface GetHomeServerCapabilitiesTask : Task { + data class Params( + val forceRefresh: Boolean + ) +} internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( private val capabilitiesAPI: CapabilitiesAPI, @@ -52,12 +56,14 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( private val userId: String ) : GetHomeServerCapabilitiesTask { - override suspend fun execute(params: Unit) { - var doRequest = false - monarchy.awaitTransaction { realm -> - val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) + override suspend fun execute(params: GetHomeServerCapabilitiesTask.Params) { + var doRequest = params.forceRefresh + if (!doRequest) { + monarchy.awaitTransaction { realm -> + val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) - doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time + doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time + } } if (!doRequest) { @@ -123,7 +129,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( } companion object { - // 8 hours like on Riot Web + // 8 hours like on Element Web private const val MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS = 8 * 60 * 60 * 1000 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index 7c38230065..bfe3799771 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -77,7 +77,7 @@ internal class DefaultSyncTask @Inject constructor( initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100) } // Maybe refresh the home server capabilities data we know - getHomeServerCapabilitiesTask.execute(Unit) + getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false)) val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt index 7c1a78d0cf..abd823fa99 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeServerSettingsViewState.kt @@ -20,8 +20,10 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.federation.FederationVersion +import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities data class HomeServerSettingsViewState( val baseUrl: String = "", + val homeServerCapabilities: HomeServerCapabilities = HomeServerCapabilities(), val federationVersion: Async = Uninitialized ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt index 4f648309f0..e90f711edc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsController.kt @@ -25,13 +25,16 @@ import im.vector.app.R import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.settingsCenteredImageItem import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.discovery.settingsSectionTitleItem import org.matrix.android.sdk.api.federation.FederationVersion +import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import javax.inject.Inject class HomeserverSettingsController @Inject constructor( + private val stringProvider: StringProvider, private val errorFormatter: ErrorFormatter ) : TypedEpoxyController() { @@ -51,15 +54,16 @@ class HomeserverSettingsController @Inject constructor( loadingItem { id("loading") } - is Fail -> + is Fail -> errorWithRetryItem { id("error") text(errorFormatter.toHumanReadable(federationVersion.error)) listener { callback?.retry() } } - is Success -> + is Success -> buildFederationVersion(federationVersion()) } + buildCapabilities(data) } private fun buildHeader(state: HomeServerSettingsViewState) { @@ -95,4 +99,22 @@ class HomeserverSettingsController @Inject constructor( helperText(federationVersion.version) } } + + private fun buildCapabilities(data: HomeServerSettingsViewState) { + settingsSectionTitleItem { + id("uploadTitle") + titleResId(R.string.settings_server_upload_size_title) + } + + val limit = data.homeServerCapabilities.maxUploadFileSize + + settingsInfoItem { + id("uploadValue") + if (limit == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) { + helperTextResId(R.string.settings_server_upload_size_unknown) + } else { + helperText(stringProvider.getString(R.string.settings_server_upload_size_content, "${limit / 1048576L} MB")) + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt index 19578aea81..17f03a3456 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt @@ -53,10 +53,26 @@ class HomeserverSettingsViewModel @AssistedInject constructor( init { setState { copy( - baseUrl = session.sessionParams.homeServerUrl + baseUrl = session.sessionParams.homeServerUrl, + homeServerCapabilities = session.getHomeServerCapabilities() ) } fetchHomeserverVersion() + refreshHomeServerCapabilities() + } + + private fun refreshHomeServerCapabilities() { + viewModelScope.launch { + runCatching { + session.refreshHomeServerCapabilities() + } + + setState { + copy( + homeServerCapabilities = session.getHomeServerCapabilities() + ) + } + } } private fun fetchHomeserverVersion() { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index d71720f64a..a1e7b01cda 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2338,6 +2338,9 @@ Server name Server version + Server file upload limit + Your homeserver accepts attachments (files, media, etc.) with a size up to %s. + The limit is unknown. No cryptographic information available From 0f328fda440f179f8906764f3ac44bb61e76eb3c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 19 Feb 2021 11:59:33 +0100 Subject: [PATCH 08/12] Add the new screen to the sanity test --- .../java/im/vector/app/ui/UiAllScreensSanityTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index 338d57fea8..a0b8e08885 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -483,6 +483,9 @@ class UiAllScreensSanityTest { clickOn(R.string.add_identity_server) pressBack() pressBack() + // Home server + clickOnPreference(R.string.settings_home_server) + pressBack() // Identity server clickOnPreference(R.string.settings_identity_server) pressBack() From 490a8551aca76bf0013f377e630138f686fa0e93 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 19 Feb 2021 12:18:14 +0100 Subject: [PATCH 09/12] Items have been reordered --- .../java/im/vector/app/ui/UiAllScreensSanityTest.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index a0b8e08885..0b93e58ee2 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -277,13 +277,18 @@ class UiAllScreensSanityTest { assertDisplayed(R.id.roomProfileAvatarView) - // Room addresses + // Leave clickListItem(R.id.matrixProfileRecyclerView, 13) + clickDialogNegativeButton() + + // Advanced + // Room addresses + clickListItem(R.id.matrixProfileRecyclerView, 15) onView(isRoot()).perform(waitForView(withText(R.string.room_alias_published_alias_title))) pressBack() // Room permissions - clickListItem(R.id.matrixProfileRecyclerView, 15) + clickListItem(R.id.matrixProfileRecyclerView, 17) onView(isRoot()).perform(waitForView(withText(R.string.room_permissions_title))) clickOn(R.string.room_permissions_change_room_avatar) clickDialogNegativeButton() @@ -292,10 +297,6 @@ class UiAllScreensSanityTest { clickOn(R.string.hide_advanced) pressBack() - // Leave - clickListItem(R.id.matrixProfileRecyclerView, 17) - clickDialogNegativeButton() - // Menu share // clickMenu(R.id.roomProfileShareAction) // pressBack() From 8580f46ec19fcab4807b7d58d35b2090e265c145 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 19 Feb 2021 12:45:52 +0100 Subject: [PATCH 10/12] Fix other issue in test --- .../java/im/vector/app/ui/UiAllScreensSanityTest.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index 0b93e58ee2..6f8056de13 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -23,7 +23,6 @@ import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.action.ViewActions.longClick import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem -import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId @@ -145,7 +144,7 @@ class UiAllScreensSanityTest { } private fun ignoreVerification() { - Thread.sleep(6000) + sleep(6000) val activity = EspressoHelper.getCurrentActivity()!! val popup = activity.findViewById(com.tapadoo.alerter.R.id.llAlertBackground) @@ -155,7 +154,7 @@ class UiAllScreensSanityTest { assertDisplayed(R.id.bottomSheetFragmentContainer) - onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000)) + onView(isRoot()).perform(SleepViewAction.sleep(2000)) clickOn(R.string.skip) assertDisplayed(R.string.are_you_sure) @@ -206,12 +205,12 @@ class UiAllScreensSanityTest { // Test quick reaction longClickOnMessage() // Add quick reaction - clickOn("πŸ‘") + clickOn("\uD83D\uDC4D️") // πŸ‘ sleep(1000) // Open reactions - longClickOn("πŸ‘") + longClickOn("\uD83D\uDC4D️") // πŸ‘ pressBack() // Test add reaction @@ -226,6 +225,8 @@ class UiAllScreensSanityTest { clickOn(R.string.edit) // TODO Cancel action writeTo(R.id.composerEditText, "Hello universe!") + // Wait a bit for the keyboard layout to update + sleep(30) clickOn(R.id.sendButton) // Open edit history longClickOnMessage("Hello universe! (edited)") From e511e7e02ed36195acacc5f80dd4a77fb0fce3f8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 19 Feb 2021 21:26:01 +0100 Subject: [PATCH 11/12] Add menu entry for dev tools (to complete existing /devtools command) --- .../features/roomprofile/RoomProfileController.kt | 14 ++++++++++++-- .../features/roomprofile/RoomProfileFragment.kt | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index 5ab86f7138..820f6848d8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -57,6 +57,7 @@ class RoomProfileController @Inject constructor( fun onRoomAliasesClicked() fun onRoomPermissionsClicked() fun onRoomIdClicked() + fun onRoomDevToolsClicked() fun onUrlInTopicLongClicked(url: String) } @@ -193,7 +194,7 @@ class RoomProfileController @Inject constructor( title = stringProvider.getString(R.string.room_settings_permissions_title), subtitle = stringProvider.getString(R.string.room_settings_permissions_subtitle), dividerColor = dividerColor, - divider = true, + divider = false, editable = true, action = { callback?.onRoomPermissionsClicked() } ) @@ -204,10 +205,19 @@ class RoomProfileController @Inject constructor( title = stringProvider.getString(R.string.room_settings_room_internal_id), subtitle = roomSummary.roomId, dividerColor = dividerColor, - divider = false, + divider = true, editable = false, action = { callback?.onRoomIdClicked() } ) + buildProfileAction( + id = "devTools", + title = stringProvider.getString(R.string.dev_tools_menu_name), + subtitle = roomSummary.roomId, + dividerColor = dividerColor, + divider = false, + editable = true, + action = { callback?.onRoomDevToolsClicked() } + ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 154483652b..16c703ea48 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -295,6 +295,10 @@ class RoomProfileFragment @Inject constructor( copyToClipboard(requireContext(), roomProfileArgs.roomId) } + override fun onRoomDevToolsClicked() { + navigator.openDevTools(requireContext(), roomProfileArgs.roomId) + } + override fun onUrlInTopicLongClicked(url: String) { copyToClipboard(requireContext(), url, true) } From 8793d263d45a712d0f3260cfe6faccc29133268d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 19 Feb 2021 21:53:10 +0100 Subject: [PATCH 12/12] Add info about room version --- .../features/roomprofile/RoomProfileController.kt | 10 ++++++++++ .../features/roomprofile/RoomProfileViewModel.kt | 14 ++++++++++++++ .../features/roomprofile/RoomProfileViewState.kt | 2 ++ vector/src/main/res/values/strings.xml | 1 + 4 files changed, 27 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index 820f6848d8..bb7d041199 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -209,6 +209,16 @@ class RoomProfileController @Inject constructor( editable = false, action = { callback?.onRoomIdClicked() } ) + data.roomCreateContent()?.roomVersion?.let { + buildProfileAction( + id = "roomVersion", + title = stringProvider.getString(R.string.room_settings_room_version_title), + subtitle = it, + dividerColor = dividerColor, + divider = true, + editable = false + ) + } buildProfileAction( id = "devTools", title = stringProvider.getString(R.string.dev_tools_menu_name), diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 02a648287f..c8bb6b5b5c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -33,13 +33,17 @@ import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.rx.RxRoom +import org.matrix.android.sdk.rx.mapOptional import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -69,10 +73,20 @@ class RoomProfileViewModel @AssistedInject constructor( init { val rxRoom = room.rx() observeRoomSummary(rxRoom) + observeRoomCreateContent(rxRoom) observeBannedRoomMembers(rxRoom) observePermissions() } + private fun observeRoomCreateContent(rxRoom: RxRoom) { + rxRoom.liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + .execute { + copy(roomCreateContent = it) + } + } + private fun observeRoomSummary(rxRoom: RxRoom) { rxRoom.liveRoomSummary() .unwrap() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt index 398982ede1..bf7cd732ef 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt @@ -22,10 +22,12 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent data class RoomProfileViewState( val roomId: String, val roomSummary: Async = Uninitialized, + val roomCreateContent: Async = Uninitialized, val bannedMembership: Async> = Uninitialized, val actionPermissions: ActionPermissions = ActionPermissions(), val isLoading: Boolean = false diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a1e7b01cda..a3eade1f02 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1117,6 +1117,7 @@ Advanced This room’s internal ID + Room version Addresses Labs These are experimental features that may break in unexpected ways. Use with caution.