From f340a19c8e36fd4e22b428f6b95838e28f9bcd2d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 16:30:45 +0100 Subject: [PATCH 01/15] Fix issue in disconnect identity server confirmation dialog content --- .../app/features/discovery/DiscoverySettingsFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 9c46fc2089..e1e1c9a537 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -167,10 +167,11 @@ class DiscoverySettingsFragment @Inject constructor( val pidList = state.emailList().orEmpty() + state.phoneNumbersList().orEmpty() val hasBoundIds = pidList.any { it.isShared() == SharedState.SHARED } + val serverUrl = state.identityServer()?.serverUrl.orEmpty() val message = if (hasBoundIds) { - getString(R.string.settings_discovery_disconnect_with_bound_pid, state.identityServer(), state.identityServer()) + getString(R.string.settings_discovery_disconnect_with_bound_pid, serverUrl, serverUrl) } else { - getString(R.string.disconnect_identity_server_dialog_content, state.identityServer()) + getString(R.string.disconnect_identity_server_dialog_content, serverUrl) } MaterialAlertDialogBuilder(requireActivity()) From d49a0dde6ec79e1782d4a667b78101d64082ee0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 18:55:43 +0100 Subject: [PATCH 02/15] Legals: Add the screen (WIP) --- .../im/vector/app/core/di/FragmentModule.kt | 6 + .../app/core/di/MavericksViewModelModule.kt | 6 + .../app/features/discovery/Extensions.kt | 37 ++++-- .../discovery/IdentityServerWithTerms.kt | 1 + .../features/settings/legals/LegalsAction.kt | 23 ++++ .../settings/legals/LegalsController.kt | 118 ++++++++++++++++++ .../settings/legals/LegalsFragment.kt | 84 +++++++++++++ .../features/settings/legals/LegalsState.kt | 28 +++++ .../settings/legals/LegalsViewEvents.kt | 23 ++++ .../settings/legals/LegalsViewModel.kt | 91 ++++++++++++++ vector/src/main/res/values/strings.xml | 7 +- .../src/main/res/xml/vector_settings_root.xml | 5 + 12 files changed, 415 insertions(+), 14 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt 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 b8d00fac5a..43bb505a5e 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 @@ -133,6 +133,7 @@ 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.legals.LegalsFragment import im.vector.app.features.settings.locale.LocalePickerFragment import im.vector.app.features.settings.notifications.VectorSettingsAdvancedNotificationPreferenceFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment @@ -699,6 +700,11 @@ interface FragmentModule { @FragmentKey(DiscoverySettingsFragment::class) fun bindDiscoverySettingsFragment(fragment: DiscoverySettingsFragment): Fragment + @Binds + @IntoMap + @FragmentKey(LegalsFragment::class) + fun bindLegalsFragment(fragment: LegalsFragment): Fragment + @Binds @IntoMap @FragmentKey(ReviewTermsFragment::class) diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index cac694e84e..37721ca9f9 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -85,6 +85,7 @@ import im.vector.app.features.settings.devtools.KeyRequestListViewModel import im.vector.app.features.settings.devtools.KeyRequestViewModel import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel import im.vector.app.features.settings.ignored.IgnoredUsersViewModel +import im.vector.app.features.settings.legals.LegalsViewModel import im.vector.app.features.settings.locale.LocalePickerViewModel import im.vector.app.features.settings.push.PushGatewaysViewModel import im.vector.app.features.settings.threepids.ThreePidsSettingsViewModel @@ -504,6 +505,11 @@ interface MavericksViewModelModule { @MavericksViewModelKey(DiscoverySettingsViewModel::class) fun discoverySettingsViewModelFactory(factory: DiscoverySettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds + @IntoMap + @MavericksViewModelKey(LegalsViewModel::class) + fun legalsViewModelFactory(factory: LegalsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds @IntoMap @MavericksViewModelKey(RoomDetailViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt index bf6bd89938..119e544414 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt @@ -23,18 +23,29 @@ import org.matrix.android.sdk.api.session.terms.TermsService suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): IdentityServerWithTerms? { val identityServerUrl = identityService().getCurrentIdentityServerUrl() return identityServerUrl?.let { - val terms = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) - .serverResponse - .getLocalizedTerms(userLanguage) - val policyUrls = terms.mapNotNull { - val name = it.localizedName ?: it.policyName - val url = it.localizedUrl - if (name == null || url == null) { - null - } else { - IdentityServerPolicy(name = name, url = url) - } - } - IdentityServerWithTerms(identityServerUrl, policyUrls) + fetchTerms(it, TermsService.ServiceType.IdentityService, userLanguage) } } + +suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): IdentityServerWithTerms { + val homeserverUrl = sessionParams.homeServerUrl + return fetchTerms(homeserverUrl, TermsService.ServiceType.IdentityService, userLanguage) +} + +private suspend fun Session.fetchTerms(serviceUrl: String, + serviceType: TermsService.ServiceType, + userLanguage: String): IdentityServerWithTerms { + val terms = getTerms(serviceType, serviceUrl.ensureProtocol()) + .serverResponse + .getLocalizedTerms(userLanguage) + val policyUrls = terms.mapNotNull { + val name = it.localizedName ?: it.policyName + val url = it.localizedUrl + if (name == null || url == null) { + null + } else { + IdentityServerPolicy(name = name, url = url) + } + } + return IdentityServerWithTerms(serviceUrl, policyUrls) +} diff --git a/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt b/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt index 36bae0d0c5..0454de70d7 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt @@ -16,6 +16,7 @@ package im.vector.app.features.discovery +// TODO Rename for more generic name data class IdentityServerWithTerms( val serverUrl: String, val policies: List diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsAction.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsAction.kt new file mode 100644 index 0000000000..424c6bb78b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsAction.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.legals + +import im.vector.app.core.platform.VectorViewModelAction + +sealed interface LegalsAction : VectorViewModelAction { + object Refresh : LegalsAction +} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt new file mode 100644 index 0000000000..29ad27b4a9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -0,0 +1,118 @@ +/* + * 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.legals + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Async +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.core.resources.StringProvider +import im.vector.app.features.discovery.IdentityServerPolicy +import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.discoveryPolicyItem +import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.discovery.settingsSectionTitleItem +import javax.inject.Inject + +class LegalsController @Inject constructor( + private val stringProvider: StringProvider, + private val errorFormatter: ErrorFormatter +) : TypedEpoxyController() { + + var listener: Listener? = null + + override fun buildModels(data: LegalsState) { + buildAppSection() + buildHomeserverSection(data) + buildIdentityServerSection(data) + } + + private fun buildAppSection() { + settingsSectionTitleItem { + id("appTitle") + titleResId(R.string.legals_application_title) + } + + // TODO + } + + private fun buildHomeserverSection(data: LegalsState) { + settingsSectionTitleItem { + id("hsServerTitle") + titleResId(R.string.legals_home_server_title) + } + + buildPolicy("hs", data.homeServer) + } + + private fun buildIdentityServerSection(data: LegalsState) { + if (data.hasIdentityServer) { + settingsSectionTitleItem { + id("idServerTitle") + titleResId(R.string.legals_identity_server_title) + } + + buildPolicy("is", data.identityServer) + } + } + + private fun buildPolicy(tag: String, serverWithTerms: Async) { + val host = this + + when (serverWithTerms) { + Uninitialized, + is Loading -> loadingItem { + id("loading_$tag") + } + is Success -> { + val policies = serverWithTerms()?.policies + if (policies.isNullOrEmpty()) { + settingsInfoItem { + id("emptyPolicy") + helperText(host.stringProvider.getString(R.string.legals_no_policy_provided)) + } + } else { + policies.forEach { policy -> + discoveryPolicyItem { + id(policy.url) + name(policy.name) + url(policy.url) + clickListener { host.listener?.openPolicy(policy) } + } + } + } + } + is Fail -> { + errorWithRetryItem { + id("errorRetry_$tag") + text(host.errorFormatter.toHumanReadable(serverWithTerms.error)) + listener { host.listener?.onTapRetry() } + } + } + } + } + + interface Listener { + fun onTapRetry() + fun openPolicy(policy: IdentityServerPolicy) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt new file mode 100644 index 0000000000..9aa7633e84 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt @@ -0,0 +1,84 @@ +/* + * 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.legals + +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.extensions.exhaustive +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.openUrlInChromeCustomTab +import im.vector.app.databinding.FragmentGenericRecyclerBinding +import im.vector.app.features.discovery.IdentityServerPolicy +import javax.inject.Inject + +class LegalsFragment @Inject constructor( + private val controller: LegalsController +) : VectorBaseFragment(), + LegalsController.Listener { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { + return FragmentGenericRecyclerBinding.inflate(inflater, container, false) + } + + private val viewModel by fragmentViewModel(LegalsViewModel::class) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + controller.listener = this + views.genericRecyclerView.configureWith(controller) + + viewModel.observeViewEvents { + when (it) { + is LegalsViewEvents.Failure -> { + displayErrorDialog(it.throwable) + } + }.exhaustive + } + } + + override fun onDestroyView() { + views.genericRecyclerView.cleanup() + controller.listener = null + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { state -> + controller.setData(state) + } + + override fun onResume() { + super.onResume() + (activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.preference_root_legals) + viewModel.handle(LegalsAction.Refresh) + } + + override fun onTapRetry() { + viewModel.handle(LegalsAction.Refresh) + } + + override fun openPolicy(policy: IdentityServerPolicy) { + openUrlInChromeCustomTab(requireContext(), null, policy.url) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt new file mode 100644 index 0000000000..fea9a06c11 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt @@ -0,0 +1,28 @@ +/* + * 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.legals + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.discovery.IdentityServerWithTerms + +data class LegalsState( + val homeServer: Async = Uninitialized, + val hasIdentityServer: Boolean = false, + val identityServer: Async = Uninitialized +) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt new file mode 100644 index 0000000000..40741a4d62 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.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.legals + +import im.vector.app.core.platform.VectorViewEvents + +sealed interface LegalsViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : LegalsViewEvents +} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt new file mode 100644 index 0000000000..7947bcc570 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt @@ -0,0 +1,91 @@ +/* + * 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.legals + +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.Success +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.fetchHomeserverWithTerms +import im.vector.app.features.discovery.fetchIdentityServerWithTerms +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session + +class LegalsViewModel @AssistedInject constructor( + @Assisted initialState: LegalsState, + private val session: Session, + private val stringProvider: StringProvider +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: LegalsState): LegalsViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + override fun handle(action: LegalsAction) { + when (action) { + LegalsAction.Refresh -> loadData() + }.exhaustive + } + + private fun loadData() = withState { state -> + loadHomeserver(state) + val url = session.identityService().getCurrentIdentityServerUrl() + if (url.isNullOrEmpty()) { + setState { copy(hasIdentityServer = false) } + } else { + setState { copy(hasIdentityServer = true) } + loadIdentityServer(state) + } + } + + private fun loadHomeserver(state: LegalsState) { + if (state.homeServer !is Success) { + setState { copy(homeServer = Loading()) } + viewModelScope.launch { + runCatching { session.fetchHomeserverWithTerms(stringProvider.getString(R.string.resources_language)) } + .fold( + onSuccess = { setState { copy(homeServer = Success(it)) } }, + onFailure = { setState { copy(homeServer = Fail(it)) } } + ) + } + } + } + + private fun loadIdentityServer(state: LegalsState) { + if (state.identityServer !is Success) { + setState { copy(identityServer = Loading()) } + viewModelScope.launch { + runCatching { session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) } + .fold( + onSuccess = { setState { copy(identityServer = Success(it)) } }, + onFailure = { setState { copy(identityServer = Fail(it)) } } + ) + } + } + } +} diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ddb731a5e0..e23cc02235 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1397,6 +1397,11 @@ Allow integrations Integration manager + ${app_name} policy + Your homeserver policy + Your identity server policy + This server does not provide any policy. + Integrations are disabled "Enable 'Allow integrations' in Settings to do this." @@ -2270,6 +2275,7 @@ Voice & Video Help & About + Legals Register token @@ -3566,7 +3572,6 @@ Manage rooms and spaces - Show all rooms in Home All rooms you’re in will be shown in Home. diff --git a/vector/src/main/res/xml/vector_settings_root.xml b/vector/src/main/res/xml/vector_settings_root.xml index 32e21b3391..712c1e7a4c 100644 --- a/vector/src/main/res/xml/vector_settings_root.xml +++ b/vector/src/main/res/xml/vector_settings_root.xml @@ -53,4 +53,9 @@ android:title="@string/preference_root_help_about" app:fragment="im.vector.app.features.settings.VectorSettingsHelpAboutFragment" /> + + \ No newline at end of file From 411fd31d4cb1051e854ec3f86a27a3f8eb4ca98f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 19:58:50 +0100 Subject: [PATCH 03/15] Legals: Trick to get the homeserver policy --- .../sdk/api/session/terms/TermsService.kt | 7 ++- .../session/terms/DefaultTermsService.kt | 53 +++++++++++++++++-- .../sdk/internal/session/terms/TermsAPI.kt | 10 ++++ .../app/features/discovery/Extensions.kt | 2 +- .../app/features/terms/ReviewTermsFragment.kt | 1 + 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt index 10ce0829d0..9898923003 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt @@ -19,7 +19,12 @@ package org.matrix.android.sdk.api.session.terms interface TermsService { enum class ServiceType { IntegrationManager, - IdentityService + IdentityService, + + /** + * It's only possible to use this value for [getTerms] + */ + Homeserver } suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt index d40fd8d076..5cdef3db0a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt @@ -18,10 +18,13 @@ package org.matrix.android.sdk.internal.session.terms import dagger.Lazy import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.data.LoginFlowTypes +import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.terms.GetTermsResponse import org.matrix.android.sdk.api.session.terms.TermsService +import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.RetrofitFactory @@ -48,11 +51,49 @@ internal class DefaultTermsService @Inject constructor( override suspend fun getTerms(serviceType: TermsService.ServiceType, baseUrl: String): GetTermsResponse { - val url = buildUrl(baseUrl, serviceType) - val termsResponse = executeRequest(null) { - termsAPI.getTerms("${url}terms") + return if (serviceType == TermsService.ServiceType.Homeserver) { + getHomeserverTerms(baseUrl) + } else { + val url = buildUrl(baseUrl, serviceType) + val termsResponse = executeRequest(null) { + termsAPI.getTerms("${url}terms") + } + GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData()) } - return GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData()) + } + + /** + * We use a trick here to get the homeserver T&C, we use the register API + */ + private suspend fun getHomeserverTerms(baseUrl: String): GetTermsResponse { + return try { + executeRequest(null) { + termsAPI.register(baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register") + } + // Return empty result if it succeed, but it should never happen + buildEmptyGetTermsResponse() + } catch (throwable: Throwable) { + @Suppress("UNCHECKED_CAST") + ((throwable.toRegistrationFlowResponse() + ?.params?.get(LoginFlowTypes.TERMS) as? JsonDict) + ?.get("policies") as? JsonDict) + ?.let { dict -> + GetTermsResponse( + serverResponse = TermsResponse( + policies = dict + ), + alreadyAcceptedTermUrls = emptySet() + ) + } + ?: buildEmptyGetTermsResponse() + } + } + + private fun buildEmptyGetTermsResponse(): GetTermsResponse { + return GetTermsResponse( + serverResponse = TermsResponse(), + alreadyAcceptedTermUrls = emptySet() + ) } override suspend fun agreeToTerms(serviceType: TermsService.ServiceType, @@ -91,7 +132,9 @@ internal class DefaultTermsService @Inject constructor( private fun buildUrl(baseUrl: String, serviceType: TermsService.ServiceType): String { val servicePath = when (serviceType) { TermsService.ServiceType.IntegrationManager -> NetworkConstants.URI_INTEGRATION_MANAGER_PATH - TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2 + TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2 + TermsService.ServiceType.Homeserver -> + error("You cannot use this API with parameter TermsService.ServiceType.Homeserver") } return "${baseUrl.ensureTrailingSlash()}$servicePath" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt index 91d27030de..fb6aff5a9e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsAPI.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.session.terms +import org.matrix.android.sdk.api.util.JsonDict +import org.matrix.android.sdk.api.util.emptyJsonDict import org.matrix.android.sdk.internal.network.HttpHeaders import retrofit2.http.Body import retrofit2.http.GET @@ -37,4 +39,12 @@ internal interface TermsAPI { suspend fun agreeToTerms(@Url url: String, @Body params: AcceptTermsBody, @Header(HttpHeaders.Authorization) token: String) + + /** + * API to retrieve the terms for a homeserver. The API /terms does not exist yet, so retrieve the terms from the login flow. + * We do not care about the result (Credentials) + */ + @POST + suspend fun register(@Url url: String, + @Body body: JsonDict = emptyJsonDict) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt index 119e544414..08712f6e68 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt @@ -29,7 +29,7 @@ suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): Identity suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): IdentityServerWithTerms { val homeserverUrl = sessionParams.homeServerUrl - return fetchTerms(homeserverUrl, TermsService.ServiceType.IdentityService, userLanguage) + return fetchTerms(homeserverUrl, TermsService.ServiceType.Homeserver, userLanguage) } private suspend fun Session.fetchTerms(serviceUrl: String, diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt index cb76e5b31f..6381854433 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt @@ -53,6 +53,7 @@ class ReviewTermsFragment @Inject constructor( termsController.description = when (reviewTermsViewModel.termsArgs.type) { TermsService.ServiceType.IdentityService -> getString(R.string.terms_description_for_identity_server) TermsService.ServiceType.IntegrationManager -> getString(R.string.terms_description_for_integration_manager) + TermsService.ServiceType.Homeserver -> error("Homeserver is not supported here") } termsController.listener = this From e1fc7cfaba947fb0178573cc0701eb8433144492 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 21:57:45 +0100 Subject: [PATCH 04/15] Rename some classes --- .../main/java/im/vector/app/core/utils/Dialogs.kt | 4 ++-- .../contactsbook/ContactsBookViewEvents.kt | 4 ++-- .../discovery/DiscoverySettingsController.kt | 2 +- .../discovery/DiscoverySettingsFragment.kt | 2 +- .../features/discovery/DiscoverySettingsState.kt | 2 +- .../discovery/DiscoverySettingsViewModel.kt | 6 +++--- .../im/vector/app/features/discovery/Extensions.kt | 10 +++++----- ...tityServerWithTerms.kt => ServerAndPolicies.kt} | 7 +++---- .../features/settings/legals/LegalsController.kt | 14 +++++++------- .../app/features/settings/legals/LegalsFragment.kt | 4 ++-- .../app/features/settings/legals/LegalsState.kt | 6 +++--- .../features/userdirectory/UserListViewEvents.kt | 4 ++-- 12 files changed, 32 insertions(+), 33 deletions(-) rename vector/src/main/java/im/vector/app/features/discovery/{IdentityServerWithTerms.kt => ServerAndPolicies.kt} (82%) diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index ad01546782..11b9a693da 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -23,7 +23,7 @@ import android.webkit.WebViewClient import android.widget.TextView import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R -import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.ServerAndPolicies import me.gujun.android.span.link import me.gujun.android.span.span @@ -45,7 +45,7 @@ fun Context.displayInWebView(url: String) { .show() } -fun Context.showIdentityServerConsentDialog(identityServerWithTerms: IdentityServerWithTerms?, +fun Context.showIdentityServerConsentDialog(identityServerWithTerms: ServerAndPolicies?, consentCallBack: (() -> Unit)) { // Build the message val content = span { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt index c7fd13a62c..cb8d137c05 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewEvents.kt @@ -17,9 +17,9 @@ package im.vector.app.features.contactsbook import im.vector.app.core.platform.VectorViewEvents -import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.ServerAndPolicies sealed class ContactsBookViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : ContactsBookViewEvents() - data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : ContactsBookViewEvents() + data class OnPoliciesRetrieved(val identityServerWithTerms: ServerAndPolicies?) : ContactsBookViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 03c5d981cb..551b72dd82 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -433,6 +433,6 @@ class DiscoverySettingsController @Inject constructor( fun onTapUpdateUserConsent(newValue: Boolean) fun onTapRetryToRetrieveBindings() fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean) - fun onPolicyTapped(policy: IdentityServerPolicy) + fun onPolicyTapped(policy: ServerPolicy) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index e1e1c9a537..2c37a19e7d 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -204,7 +204,7 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = newExpandedState)) } - override fun onPolicyTapped(policy: IdentityServerPolicy) { + override fun onPolicyTapped(policy: ServerPolicy) { openUrlInChromeCustomTab(requireContext(), null, policy.url) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt index da8603a437..e44caa4121 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsState.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class DiscoverySettingsState( - val identityServer: Async = Uninitialized, + val identityServer: Async = Uninitialized, val emailList: Async> = Uninitialized, val phoneNumbersList: Async> = Uninitialized, // Can be true if terms are updated diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 4eb3fada28..19f233fe98 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -78,7 +78,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( init { setState { copy( - identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { IdentityServerWithTerms(it, emptyList()) }), + identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { ServerAndPolicies(it, emptyList()) }), userConsent = identityService.getUserConsent() ) } @@ -151,7 +151,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( val data = session.identityService().setNewIdentityServer(action.url) setState { copy( - identityServer = Success(IdentityServerWithTerms(data, emptyList())), + identityServer = Success(ServerAndPolicies(data, emptyList())), userConsent = false ) } @@ -401,7 +401,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( } } - private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? { + private suspend fun fetchIdentityServerWithTerms(): ServerAndPolicies? { return session.fetchIdentityServerWithTerms(stringProvider.getString(R.string.resources_language)) } } diff --git a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt index 08712f6e68..1360c9e95d 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt @@ -20,21 +20,21 @@ import im.vector.app.core.utils.ensureProtocol import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.terms.TermsService -suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): IdentityServerWithTerms? { +suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): ServerAndPolicies? { val identityServerUrl = identityService().getCurrentIdentityServerUrl() return identityServerUrl?.let { fetchTerms(it, TermsService.ServiceType.IdentityService, userLanguage) } } -suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): IdentityServerWithTerms { +suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): ServerAndPolicies { val homeserverUrl = sessionParams.homeServerUrl return fetchTerms(homeserverUrl, TermsService.ServiceType.Homeserver, userLanguage) } private suspend fun Session.fetchTerms(serviceUrl: String, serviceType: TermsService.ServiceType, - userLanguage: String): IdentityServerWithTerms { + userLanguage: String): ServerAndPolicies { val terms = getTerms(serviceType, serviceUrl.ensureProtocol()) .serverResponse .getLocalizedTerms(userLanguage) @@ -44,8 +44,8 @@ private suspend fun Session.fetchTerms(serviceUrl: String, if (name == null || url == null) { null } else { - IdentityServerPolicy(name = name, url = url) + ServerPolicy(name = name, url = url) } } - return IdentityServerWithTerms(serviceUrl, policyUrls) + return ServerAndPolicies(serviceUrl, policyUrls) } diff --git a/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt b/vector/src/main/java/im/vector/app/features/discovery/ServerAndPolicies.kt similarity index 82% rename from vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt rename to vector/src/main/java/im/vector/app/features/discovery/ServerAndPolicies.kt index 0454de70d7..17f1b0cff9 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/IdentityServerWithTerms.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/ServerAndPolicies.kt @@ -16,13 +16,12 @@ package im.vector.app.features.discovery -// TODO Rename for more generic name -data class IdentityServerWithTerms( +data class ServerAndPolicies( val serverUrl: String, - val policies: List + val policies: List ) -data class IdentityServerPolicy( +data class ServerPolicy( val name: String, val url: String ) diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt index 29ad27b4a9..9bfbea6a42 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -26,8 +26,8 @@ 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.IdentityServerPolicy -import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.ServerPolicy +import im.vector.app.features.discovery.ServerAndPolicies import im.vector.app.features.discovery.discoveryPolicyItem import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.discovery.settingsSectionTitleItem @@ -75,16 +75,16 @@ class LegalsController @Inject constructor( } } - private fun buildPolicy(tag: String, serverWithTerms: Async) { + private fun buildPolicy(tag: String, serverAndPolicies: Async) { val host = this - when (serverWithTerms) { + when (serverAndPolicies) { Uninitialized, is Loading -> loadingItem { id("loading_$tag") } is Success -> { - val policies = serverWithTerms()?.policies + val policies = serverAndPolicies()?.policies if (policies.isNullOrEmpty()) { settingsInfoItem { id("emptyPolicy") @@ -104,7 +104,7 @@ class LegalsController @Inject constructor( is Fail -> { errorWithRetryItem { id("errorRetry_$tag") - text(host.errorFormatter.toHumanReadable(serverWithTerms.error)) + text(host.errorFormatter.toHumanReadable(serverAndPolicies.error)) listener { host.listener?.onTapRetry() } } } @@ -113,6 +113,6 @@ class LegalsController @Inject constructor( interface Listener { fun onTapRetry() - fun openPolicy(policy: IdentityServerPolicy) + fun openPolicy(policy: ServerPolicy) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt index 9aa7633e84..c6b5c2bf1c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt @@ -29,7 +29,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentGenericRecyclerBinding -import im.vector.app.features.discovery.IdentityServerPolicy +import im.vector.app.features.discovery.ServerPolicy import javax.inject.Inject class LegalsFragment @Inject constructor( @@ -78,7 +78,7 @@ class LegalsFragment @Inject constructor( viewModel.handle(LegalsAction.Refresh) } - override fun openPolicy(policy: IdentityServerPolicy) { + override fun openPolicy(policy: ServerPolicy) { openUrlInChromeCustomTab(requireContext(), null, policy.url) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt index fea9a06c11..ad01f85338 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsState.kt @@ -19,10 +19,10 @@ package im.vector.app.features.settings.legals import com.airbnb.mvrx.Async import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized -import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.ServerAndPolicies data class LegalsState( - val homeServer: Async = Uninitialized, + val homeServer: Async = Uninitialized, val hasIdentityServer: Boolean = false, - val identityServer: Async = Uninitialized + val identityServer: Async = Uninitialized ) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt index ffda0dd505..ef29e8015a 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewEvents.kt @@ -17,13 +17,13 @@ package im.vector.app.features.userdirectory import im.vector.app.core.platform.VectorViewEvents -import im.vector.app.features.discovery.IdentityServerWithTerms +import im.vector.app.features.discovery.ServerAndPolicies /** * Transient events for invite users to room screen */ sealed class UserListViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : UserListViewEvents() - data class OnPoliciesRetrieved(val identityServerWithTerms: IdentityServerWithTerms?) : UserListViewEvents() + data class OnPoliciesRetrieved(val identityServerWithTerms: ServerAndPolicies?) : UserListViewEvents() data class OpenShareMatrixToLink(val link: String) : UserListViewEvents() } From e91e26ebfb5ee645acc2913909f26efd24aac9ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 22:17:51 +0100 Subject: [PATCH 05/15] Legals: Move the 3 element links to the new legal screen --- .../features/settings/VectorPreferences.kt | 3 -- .../VectorSettingsHelpAboutFragment.kt | 22 ----------- .../features/settings/VectorSettingsUrls.kt | 1 - .../features/settings/legals/ElementLegals.kt | 38 +++++++++++++++++++ .../settings/legals/LegalsController.kt | 33 +++++++++------- .../res/xml/vector_settings_help_about.xml | 12 ------ 6 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/ElementLegals.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 47c9af3168..5bdd961277 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -42,13 +42,10 @@ class VectorPreferences @Inject constructor(private val context: Context) { const val SETTINGS_LOGGED_IN_PREFERENCE_KEY = "SETTINGS_LOGGED_IN_PREFERENCE_KEY" const val SETTINGS_HOME_SERVER_PREFERENCE_KEY = "SETTINGS_HOME_SERVER_PREFERENCE_KEY" const val SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY = "SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY" - const val SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY" - const val SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY" const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY" const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY" const val SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY" - const val SETTINGS_COPYRIGHT_PREFERENCE_KEY = "SETTINGS_COPYRIGHT_PREFERENCE_KEY" const val SETTINGS_CLEAR_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_CACHE_PREFERENCE_KEY" const val SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY" const val SETTINGS_USER_SETTINGS_PREFERENCE_KEY = "SETTINGS_USER_SETTINGS_PREFERENCE_KEY" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt index 03b7c16274..43325b6623 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt @@ -24,7 +24,6 @@ import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.displayInWebView import im.vector.app.core.utils.openAppSettingsPage -import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.version.VersionProvider import im.vector.app.openOssLicensesMenuActivity import org.matrix.android.sdk.api.Matrix @@ -77,27 +76,6 @@ class VectorSettingsHelpAboutFragment @Inject constructor( findPreference(VectorPreferences.SETTINGS_OLM_VERSION_PREFERENCE_KEY)!! .summary = session.cryptoService().getCryptoVersion(requireContext(), false) - // copyright - findPreference(VectorPreferences.SETTINGS_COPYRIGHT_PREFERENCE_KEY)!! - .onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.COPYRIGHT) - false - } - - // terms & conditions - findPreference(VectorPreferences.SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY)!! - .onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.TAC) - false - } - - // privacy policy - findPreference(VectorPreferences.SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY)!! - .onPreferenceClickListener = Preference.OnPreferenceClickListener { - openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.PRIVACY_POLICY) - false - } - // third party notice findPreference(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!! .onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt index c5088aac6d..b3925c32dc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt @@ -17,7 +17,6 @@ package im.vector.app.features.settings object VectorSettingsUrls { - const val COPYRIGHT = "https://element.io/copyright" const val TAC = "https://element.io/terms-of-service" const val PRIVACY_POLICY = "https://element.io/privacy" diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/ElementLegals.kt b/vector/src/main/java/im/vector/app/features/settings/legals/ElementLegals.kt new file mode 100644 index 0000000000..de59f36604 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/legals/ElementLegals.kt @@ -0,0 +1,38 @@ +/* + * 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.legals + +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.ServerPolicy +import im.vector.app.features.settings.VectorSettingsUrls +import javax.inject.Inject + +class ElementLegals @Inject constructor( + private val stringProvider: StringProvider +) { + /** + * Use ServerPolicy model + */ + fun getData(): List { + return listOf( + ServerPolicy(stringProvider.getString(R.string.settings_copyright), VectorSettingsUrls.COPYRIGHT), + ServerPolicy(stringProvider.getString(R.string.settings_app_term_conditions), VectorSettingsUrls.TAC), + ServerPolicy(stringProvider.getString(R.string.settings_privacy_policy), VectorSettingsUrls.PRIVACY_POLICY) + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt index 9bfbea6a42..5470bb3ace 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -26,8 +26,8 @@ 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.ServerPolicy import im.vector.app.features.discovery.ServerAndPolicies +import im.vector.app.features.discovery.ServerPolicy import im.vector.app.features.discovery.discoveryPolicyItem import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.discovery.settingsSectionTitleItem @@ -35,6 +35,7 @@ import javax.inject.Inject class LegalsController @Inject constructor( private val stringProvider: StringProvider, + private val elementLegals: ElementLegals, private val errorFormatter: ErrorFormatter ) : TypedEpoxyController() { @@ -52,7 +53,7 @@ class LegalsController @Inject constructor( titleResId(R.string.legals_application_title) } - // TODO + buildPolicies("el", elementLegals.getData()) } private fun buildHomeserverSection(data: LegalsState) { @@ -61,7 +62,7 @@ class LegalsController @Inject constructor( titleResId(R.string.legals_home_server_title) } - buildPolicy("hs", data.homeServer) + buildPolicyAsync("hs", data.homeServer) } private fun buildIdentityServerSection(data: LegalsState) { @@ -71,11 +72,11 @@ class LegalsController @Inject constructor( titleResId(R.string.legals_identity_server_title) } - buildPolicy("is", data.identityServer) + buildPolicyAsync("is", data.identityServer) } } - private fun buildPolicy(tag: String, serverAndPolicies: Async) { + private fun buildPolicyAsync(tag: String, serverAndPolicies: Async) { val host = this when (serverAndPolicies) { @@ -91,14 +92,7 @@ class LegalsController @Inject constructor( helperText(host.stringProvider.getString(R.string.legals_no_policy_provided)) } } else { - policies.forEach { policy -> - discoveryPolicyItem { - id(policy.url) - name(policy.name) - url(policy.url) - clickListener { host.listener?.openPolicy(policy) } - } - } + buildPolicies(tag, policies) } } is Fail -> { @@ -111,6 +105,19 @@ class LegalsController @Inject constructor( } } + private fun buildPolicies(tag: String, policies: List) { + val host = this + + policies.forEach { policy -> + discoveryPolicyItem { + id(tag + policy.url) + name(policy.name) + url(policy.url) + clickListener { host.listener?.openPolicy(policy) } + } + } + } + interface Listener { fun onTapRetry() fun openPolicy(policy: ServerPolicy) diff --git a/vector/src/main/res/xml/vector_settings_help_about.xml b/vector/src/main/res/xml/vector_settings_help_about.xml index b36fba05c2..9171d1e6fe 100644 --- a/vector/src/main/res/xml/vector_settings_help_about.xml +++ b/vector/src/main/res/xml/vector_settings_help_about.xml @@ -23,18 +23,6 @@ android:title="@string/settings_olm_version" tools:summary="7.8.9" /> - - - - - - From a8c921ef031f7e7540808bf31e3d91cc7ec9d7bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 22:21:18 +0100 Subject: [PATCH 06/15] Add some space between the 2 TextViews, and improve the layout --- .../main/res/layout/item_discovery_policy.xml | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml index 5d8c7159e9..d68b1e63a1 100644 --- a/vector/src/main/res/layout/item_discovery_policy.xml +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -5,8 +5,20 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" + android:minHeight="64dp" android:padding="16dp"> + + + tools:text="Copyright" /> + tools:text="https://element.io/copyright" /> Date: Wed, 8 Dec 2021 22:46:25 +0100 Subject: [PATCH 07/15] Legals: Move the 2 copyrights items to the new legal screen --- .../features/discovery/DiscoveryPolicyItem.kt | 3 +- .../features/settings/VectorPreferences.kt | 2 -- .../VectorSettingsHelpAboutFragment.kt | 22 --------------- .../settings/legals/LegalsController.kt | 27 ++++++++++++++++++ .../settings/legals/LegalsFragment.kt | 28 ++++++++++++++++++- vector/src/main/res/values/strings.xml | 1 + .../res/xml/vector_settings_help_about.xml | 10 ------- 7 files changed, 57 insertions(+), 36 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt index c97a2286ae..4df4146d2f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoveryPolicyItem.kt @@ -24,6 +24,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setTextOrHide @EpoxyModelClass(layout = R.layout.item_discovery_policy) abstract class DiscoveryPolicyItem : EpoxyModelWithHolder() { @@ -40,7 +41,7 @@ abstract class DiscoveryPolicyItem : EpoxyModelWithHolder(APP_INFO_LINK_PREFERENCE_KEY)!! @@ -75,23 +70,6 @@ class VectorSettingsHelpAboutFragment @Inject constructor( // olm version findPreference(VectorPreferences.SETTINGS_OLM_VERSION_PREFERENCE_KEY)!! .summary = session.cryptoService().getCryptoVersion(requireContext(), false) - - // third party notice - findPreference(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!! - .onPreferenceClickListener = Preference.OnPreferenceClickListener { - if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) { - activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES) - } - false - } - - // Note: preference is not visible on F-Droid build - findPreference(VectorPreferences.SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!! - .onPreferenceClickListener = Preference.OnPreferenceClickListener { - // See https://developers.google.com/android/guides/opensource - openOssLicensesMenuActivity(requireActivity()) - false - } } companion object { diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt index 5470bb3ace..754a31be31 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.settings.legals +import android.content.res.Resources import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -35,6 +36,7 @@ import javax.inject.Inject class LegalsController @Inject constructor( private val stringProvider: StringProvider, + private val resources: Resources, private val elementLegals: ElementLegals, private val errorFormatter: ErrorFormatter ) : TypedEpoxyController() { @@ -45,6 +47,7 @@ class LegalsController @Inject constructor( buildAppSection() buildHomeserverSection(data) buildIdentityServerSection(data) + buildThirdPartyNotices() } private fun buildAppSection() { @@ -118,8 +121,32 @@ class LegalsController @Inject constructor( } } + private fun buildThirdPartyNotices() { + val host = this + settingsSectionTitleItem { + id("appTitle") + titleResId(R.string.legals_third_party_notices) + } + + discoveryPolicyItem { + id("elcp1") + name(host.stringProvider.getString(R.string.settings_third_party_notices)) + clickListener { host.listener?.openThirdPartyNotice() } + } + // Only on Gplay + if (resources.getBoolean(R.bool.isGplay)) { + discoveryPolicyItem { + id("elcp2") + name(host.stringProvider.getString(R.string.settings_other_third_party_notices)) + clickListener { host.listener?.openThirdPartyNoticeGplay() } + } + } + } + interface Listener { fun onTapRetry() fun openPolicy(policy: ServerPolicy) + fun openThirdPartyNotice() + fun openThirdPartyNoticeGplay() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt index c6b5c2bf1c..6a3db28702 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt @@ -27,9 +27,13 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.FirstThrottler +import im.vector.app.core.utils.displayInWebView import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.discovery.ServerPolicy +import im.vector.app.features.settings.VectorSettingsUrls +import im.vector.app.openOssLicensesMenuActivity import javax.inject.Inject class LegalsFragment @Inject constructor( @@ -42,6 +46,7 @@ class LegalsFragment @Inject constructor( } private val viewModel by fragmentViewModel(LegalsViewModel::class) + private val firstThrottler = FirstThrottler(1000) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -79,6 +84,27 @@ class LegalsFragment @Inject constructor( } override fun openPolicy(policy: ServerPolicy) { - openUrlInChromeCustomTab(requireContext(), null, policy.url) + openUrl(policy.url) + } + + override fun openThirdPartyNotice() { + openUrl(VectorSettingsUrls.THIRD_PARTY_LICENSES) + } + + private fun openUrl(url: String) { + if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) { + if (url.startsWith("file://")) { + activity?.displayInWebView(url) + } else { + openUrlInChromeCustomTab(requireContext(), null, url) + } + } + } + + override fun openThirdPartyNoticeGplay() { + if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) { + // See https://developers.google.com/android/guides/opensource + openOssLicensesMenuActivity(requireActivity()) + } } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index e23cc02235..2759f0ebc5 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1400,6 +1400,7 @@ ${app_name} policy Your homeserver policy Your identity server policy + Third party libraries This server does not provide any policy. Integrations are disabled diff --git a/vector/src/main/res/xml/vector_settings_help_about.xml b/vector/src/main/res/xml/vector_settings_help_about.xml index 9171d1e6fe..c532536785 100644 --- a/vector/src/main/res/xml/vector_settings_help_about.xml +++ b/vector/src/main/res/xml/vector_settings_help_about.xml @@ -1,6 +1,5 @@ - - - - \ No newline at end of file From eed7d15b0e6c811337491af8b86ceb1ca31df938 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 22:54:15 +0100 Subject: [PATCH 08/15] Legals: only display external URLs. --- .../im/vector/app/features/settings/legals/LegalsController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt index 754a31be31..fa80bf2e9e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -115,7 +115,7 @@ class LegalsController @Inject constructor( discoveryPolicyItem { id(tag + policy.url) name(policy.name) - url(policy.url) + url(policy.url.takeIf { it.startsWith("http") }) clickListener { host.listener?.openPolicy(policy) } } } From 50a84a6c56b3df98e8766ad3a75b2d7073134aae Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 22:56:45 +0100 Subject: [PATCH 09/15] Use same height than the loading item to avoid dynamic resizing --- vector/src/main/res/layout/item_discovery_policy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml index d68b1e63a1..d1d003d1ef 100644 --- a/vector/src/main/res/layout/item_discovery_policy.xml +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" - android:minHeight="64dp" + android:minHeight="80dp" android:padding="16dp"> Date: Wed, 8 Dec 2021 22:59:01 +0100 Subject: [PATCH 10/15] Color for links --- vector/src/main/res/layout/item_discovery_policy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/layout/item_discovery_policy.xml b/vector/src/main/res/layout/item_discovery_policy.xml index d1d003d1ef..86333bdee8 100644 --- a/vector/src/main/res/layout/item_discovery_policy.xml +++ b/vector/src/main/res/layout/item_discovery_policy.xml @@ -37,7 +37,7 @@ android:layout_height="wrap_content" android:paddingStart="0dp" android:paddingEnd="8dp" - android:textColor="?vctr_content_secondary" + android:textColor="?android:textColorLink" tools:text="https://element.io/copyright" /> From 17c4c1e9b8e6adbbed9491252253b3744a9931c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 23:01:29 +0100 Subject: [PATCH 11/15] Changelog --- changelog.d/4660.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4660.feature diff --git a/changelog.d/4660.feature b/changelog.d/4660.feature new file mode 100644 index 0000000000..4eca82eaf5 --- /dev/null +++ b/changelog.d/4660.feature @@ -0,0 +1 @@ +Create a legal screen in the setting to group all the different policies. \ No newline at end of file From 7f7199abd4548936be07a6f4c816f6347123214a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 23:15:57 +0100 Subject: [PATCH 12/15] Add a help section in the settings. --- changelog.d/4638.feature | 1 + .../features/settings/VectorPreferences.kt | 1 + .../VectorSettingsHelpAboutFragment.kt | 13 +++++ .../features/settings/VectorSettingsUrls.kt | 1 + vector/src/main/res/values/strings.xml | 5 ++ .../res/xml/vector_settings_help_about.xml | 49 +++++++++++++------ 6 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 changelog.d/4638.feature diff --git a/changelog.d/4638.feature b/changelog.d/4638.feature new file mode 100644 index 0000000000..0f8bd36465 --- /dev/null +++ b/changelog.d/4638.feature @@ -0,0 +1 @@ +Add a help section in the settings. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 8db2d777e1..c9464550ac 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -35,6 +35,7 @@ import javax.inject.Inject class VectorPreferences @Inject constructor(private val context: Context) { companion object { + const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY" const val SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY = "SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY" const val SETTINGS_VERSION_PREFERENCE_KEY = "SETTINGS_VERSION_PREFERENCE_KEY" const val SETTINGS_SDK_VERSION_PREFERENCE_KEY = "SETTINGS_SDK_VERSION_PREFERENCE_KEY" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt index 86b454a243..31d9cf0426 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt @@ -20,8 +20,10 @@ import androidx.preference.Preference import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.preference.VectorPreference +import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.openAppSettingsPage +import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.version.VersionProvider import org.matrix.android.sdk.api.Matrix import javax.inject.Inject @@ -33,7 +35,18 @@ class VectorSettingsHelpAboutFragment @Inject constructor( override var titleRes = R.string.preference_root_help_about override val preferenceXmlRes = R.xml.vector_settings_help_about + private val firstThrottler = FirstThrottler(1000) + override fun bindPref() { + // Help + findPreference(VectorPreferences.SETTINGS_HELP_PREFERENCE_KEY)!! + .onPreferenceClickListener = Preference.OnPreferenceClickListener { + if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) { + openUrlInChromeCustomTab(requireContext(), null, VectorSettingsUrls.HELP) + } + false + } + // preference to start the App info screen, to facilitate App permissions access findPreference(APP_INFO_LINK_PREFERENCE_KEY)!! .onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt index b3925c32dc..09249c4957 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsUrls.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings object VectorSettingsUrls { + const val HELP = "https://element.io/help" const val COPYRIGHT = "https://element.io/copyright" const val TAC = "https://element.io/terms-of-service" const val PRIVACY_POLICY = "https://element.io/privacy" diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 2759f0ebc5..97bf7a7d6a 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2278,6 +2278,11 @@ Help & About Legals + Help + Help and support + Get help with using Element + Versions + System settings Register token diff --git a/vector/src/main/res/xml/vector_settings_help_about.xml b/vector/src/main/res/xml/vector_settings_help_about.xml index c532536785..0388b545b7 100644 --- a/vector/src/main/res/xml/vector_settings_help_about.xml +++ b/vector/src/main/res/xml/vector_settings_help_about.xml @@ -2,24 +2,41 @@ - + - + - + - + + + + + + + + + + + + + + + \ No newline at end of file From 72bb58645b37615b10ea3a8e8de2eda7e2763850 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 8 Dec 2021 23:41:58 +0100 Subject: [PATCH 13/15] Auto-review --- .../settings/legals/LegalsController.kt | 6 ++--- .../settings/legals/LegalsFragment.kt | 9 -------- .../settings/legals/LegalsViewEvents.kt | 23 ------------------- .../settings/legals/LegalsViewModel.kt | 3 ++- 4 files changed, 5 insertions(+), 36 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt index fa80bf2e9e..98e77ae7d5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsController.kt @@ -124,19 +124,19 @@ class LegalsController @Inject constructor( private fun buildThirdPartyNotices() { val host = this settingsSectionTitleItem { - id("appTitle") + id("thirdTitle") titleResId(R.string.legals_third_party_notices) } discoveryPolicyItem { - id("elcp1") + id("eltpn1") name(host.stringProvider.getString(R.string.settings_third_party_notices)) clickListener { host.listener?.openThirdPartyNotice() } } // Only on Gplay if (resources.getBoolean(R.bool.isGplay)) { discoveryPolicyItem { - id("elcp2") + id("eltpn2") name(host.stringProvider.getString(R.string.settings_other_third_party_notices)) clickListener { host.listener?.openThirdPartyNoticeGplay() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt index 6a3db28702..f9b50bdead 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsFragment.kt @@ -25,7 +25,6 @@ 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.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.FirstThrottler import im.vector.app.core.utils.displayInWebView @@ -53,14 +52,6 @@ class LegalsFragment @Inject constructor( controller.listener = this views.genericRecyclerView.configureWith(controller) - - viewModel.observeViewEvents { - when (it) { - is LegalsViewEvents.Failure -> { - displayErrorDialog(it.throwable) - } - }.exhaustive - } } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt deleted file mode 100644 index 40741a4d62..0000000000 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewEvents.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.legals - -import im.vector.app.core.platform.VectorViewEvents - -sealed interface LegalsViewEvents : VectorViewEvents { - data class Failure(val throwable: Throwable) : LegalsViewEvents -} diff --git a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt index 7947bcc570..9d58535490 100644 --- a/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/legals/LegalsViewModel.kt @@ -26,6 +26,7 @@ import im.vector.app.R import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.fetchHomeserverWithTerms @@ -37,7 +38,7 @@ class LegalsViewModel @AssistedInject constructor( @Assisted initialState: LegalsState, private val session: Session, private val stringProvider: StringProvider -) : VectorViewModel(initialState) { +) : VectorViewModel(initialState) { @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { From 6ba5c7af27276ccb4e5d83baaed65a88c5cdeb70 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Dec 2021 10:39:50 +0100 Subject: [PATCH 14/15] Legals: update setting icon --- .../src/main/res/drawable/ic_settings_root_legals.xml | 10 ++++++++++ vector/src/main/res/xml/vector_settings_root.xml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/res/drawable/ic_settings_root_legals.xml diff --git a/vector/src/main/res/drawable/ic_settings_root_legals.xml b/vector/src/main/res/drawable/ic_settings_root_legals.xml new file mode 100644 index 0000000000..8c5049b36e --- /dev/null +++ b/vector/src/main/res/drawable/ic_settings_root_legals.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/xml/vector_settings_root.xml b/vector/src/main/res/xml/vector_settings_root.xml index 712c1e7a4c..520c4595ca 100644 --- a/vector/src/main/res/xml/vector_settings_root.xml +++ b/vector/src/main/res/xml/vector_settings_root.xml @@ -54,7 +54,7 @@ app:fragment="im.vector.app.features.settings.VectorSettingsHelpAboutFragment" /> From bf7907a119fd8abd4c18bbcd1f13ef4ab4305ebb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Dec 2021 12:15:19 +0100 Subject: [PATCH 15/15] Legals: improve API to get homeserver terms --- .../sdk/api/session/terms/TermsService.kt | 15 ++++--- .../session/terms/DefaultTermsService.kt | 43 ++++++------------- .../app/features/discovery/Extensions.kt | 24 ++++++----- .../app/features/terms/ReviewTermsFragment.kt | 1 - 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt index 9898923003..e64cf1872e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt @@ -16,15 +16,12 @@ package org.matrix.android.sdk.api.session.terms +import org.matrix.android.sdk.internal.session.terms.TermsResponse + interface TermsService { enum class ServiceType { IntegrationManager, - IdentityService, - - /** - * It's only possible to use this value for [getTerms] - */ - Homeserver + IdentityService } suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse @@ -33,4 +30,10 @@ interface TermsService { baseUrl: String, agreedUrls: List, token: String?) + + /** + * Get the homeserver terms, from the register API. + * Will be updated once https://github.com/matrix-org/matrix-doc/pull/3012 will be implemented. + */ + suspend fun getHomeserverTerms(baseUrl: String): TermsResponse } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt index 5cdef3db0a..c52c6a404e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt @@ -51,51 +51,34 @@ internal class DefaultTermsService @Inject constructor( override suspend fun getTerms(serviceType: TermsService.ServiceType, baseUrl: String): GetTermsResponse { - return if (serviceType == TermsService.ServiceType.Homeserver) { - getHomeserverTerms(baseUrl) - } else { - val url = buildUrl(baseUrl, serviceType) - val termsResponse = executeRequest(null) { - termsAPI.getTerms("${url}terms") - } - GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData()) + val url = buildUrl(baseUrl, serviceType) + val termsResponse = executeRequest(null) { + termsAPI.getTerms("${url}terms") } + return GetTermsResponse(termsResponse, getAlreadyAcceptedTermUrlsFromAccountData()) } /** * We use a trick here to get the homeserver T&C, we use the register API */ - private suspend fun getHomeserverTerms(baseUrl: String): GetTermsResponse { + override suspend fun getHomeserverTerms(baseUrl: String): TermsResponse { return try { executeRequest(null) { termsAPI.register(baseUrl + NetworkConstants.URI_API_PREFIX_PATH_R0 + "register") } // Return empty result if it succeed, but it should never happen - buildEmptyGetTermsResponse() + TermsResponse() } catch (throwable: Throwable) { @Suppress("UNCHECKED_CAST") - ((throwable.toRegistrationFlowResponse() - ?.params?.get(LoginFlowTypes.TERMS) as? JsonDict) - ?.get("policies") as? JsonDict) - ?.let { dict -> - GetTermsResponse( - serverResponse = TermsResponse( - policies = dict - ), - alreadyAcceptedTermUrls = emptySet() - ) - } - ?: buildEmptyGetTermsResponse() + TermsResponse( + policies = (throwable.toRegistrationFlowResponse() + ?.params + ?.get(LoginFlowTypes.TERMS) as? JsonDict) + ?.get("policies") as? JsonDict + ) } } - private fun buildEmptyGetTermsResponse(): GetTermsResponse { - return GetTermsResponse( - serverResponse = TermsResponse(), - alreadyAcceptedTermUrls = emptySet() - ) - } - override suspend fun agreeToTerms(serviceType: TermsService.ServiceType, baseUrl: String, agreedUrls: List, @@ -133,8 +116,6 @@ internal class DefaultTermsService @Inject constructor( val servicePath = when (serviceType) { TermsService.ServiceType.IntegrationManager -> NetworkConstants.URI_INTEGRATION_MANAGER_PATH TermsService.ServiceType.IdentityService -> NetworkConstants.URI_IDENTITY_PATH_V2 - TermsService.ServiceType.Homeserver -> - error("You cannot use this API with parameter TermsService.ServiceType.Homeserver") } return "${baseUrl.ensureTrailingSlash()}$servicePath" } diff --git a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt index 1360c9e95d..24d675695b 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/Extensions.kt @@ -19,25 +19,27 @@ package im.vector.app.features.discovery import im.vector.app.core.utils.ensureProtocol import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.terms.TermsService +import org.matrix.android.sdk.internal.session.terms.TermsResponse suspend fun Session.fetchIdentityServerWithTerms(userLanguage: String): ServerAndPolicies? { - val identityServerUrl = identityService().getCurrentIdentityServerUrl() - return identityServerUrl?.let { - fetchTerms(it, TermsService.ServiceType.IdentityService, userLanguage) - } + return identityService().getCurrentIdentityServerUrl() + ?.let { identityServerUrl -> + val termsResponse = getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol()) + .serverResponse + buildServerAndPolicies(identityServerUrl, termsResponse, userLanguage) + } } suspend fun Session.fetchHomeserverWithTerms(userLanguage: String): ServerAndPolicies { val homeserverUrl = sessionParams.homeServerUrl - return fetchTerms(homeserverUrl, TermsService.ServiceType.Homeserver, userLanguage) + val terms = getHomeserverTerms(homeserverUrl.ensureProtocol()) + return buildServerAndPolicies(homeserverUrl, terms, userLanguage) } -private suspend fun Session.fetchTerms(serviceUrl: String, - serviceType: TermsService.ServiceType, - userLanguage: String): ServerAndPolicies { - val terms = getTerms(serviceType, serviceUrl.ensureProtocol()) - .serverResponse - .getLocalizedTerms(userLanguage) +private fun buildServerAndPolicies(serviceUrl: String, + termsResponse: TermsResponse, + userLanguage: String): ServerAndPolicies { + val terms = termsResponse.getLocalizedTerms(userLanguage) val policyUrls = terms.mapNotNull { val name = it.localizedName ?: it.policyName val url = it.localizedUrl diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt index 6381854433..cb76e5b31f 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt @@ -53,7 +53,6 @@ class ReviewTermsFragment @Inject constructor( termsController.description = when (reviewTermsViewModel.termsArgs.type) { TermsService.ServiceType.IdentityService -> getString(R.string.terms_description_for_identity_server) TermsService.ServiceType.IntegrationManager -> getString(R.string.terms_description_for_integration_manager) - TermsService.ServiceType.Homeserver -> error("Homeserver is not supported here") } termsController.listener = this