Legals: Add the screen (WIP)
This commit is contained in:
parent
f340a19c8e
commit
d49a0dde6e
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<IdentityServerPolicy>
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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<LegalsState>() {
|
||||
|
||||
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<IdentityServerWithTerms?>) {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<FragmentGenericRecyclerBinding>(),
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<IdentityServerWithTerms?> = Uninitialized,
|
||||
val hasIdentityServer: Boolean = false,
|
||||
val identityServer: Async<IdentityServerWithTerms?> = Uninitialized
|
||||
) : MavericksState
|
|
@ -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
|
||||
}
|
|
@ -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<LegalsState, LegalsAction, LegalsViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<LegalsViewModel, LegalsState> {
|
||||
override fun create(initialState: LegalsState): LegalsViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<LegalsViewModel, LegalsState> 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)) } }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1397,6 +1397,11 @@
|
|||
<string name="settings_integration_allow">Allow integrations</string>
|
||||
<string name="settings_integration_manager">Integration manager</string>
|
||||
|
||||
<string name="template_legals_application_title">${app_name} policy</string>
|
||||
<string name="legals_home_server_title">Your homeserver policy</string>
|
||||
<string name="legals_identity_server_title">Your identity server policy</string>
|
||||
<string name="legals_no_policy_provided">This server does not provide any policy.</string>
|
||||
|
||||
<string name="disabled_integration_dialog_title">Integrations are disabled</string>
|
||||
<string name="disabled_integration_dialog_content">"Enable 'Allow integrations' in Settings to do this."</string>
|
||||
|
||||
|
@ -2270,6 +2275,7 @@
|
|||
|
||||
<string name="preference_voice_and_video">Voice & Video</string>
|
||||
<string name="preference_root_help_about">Help & About</string>
|
||||
<string name="preference_root_legals">Legals</string>
|
||||
|
||||
|
||||
<string name="settings_troubleshoot_test_token_registration_quick_fix">Register token</string>
|
||||
|
@ -3566,7 +3572,6 @@
|
|||
<string name="space_manage_rooms_and_spaces">Manage rooms and spaces</string>
|
||||
|
||||
|
||||
|
||||
<string name="preference_show_all_rooms_in_home">Show all rooms in Home</string>
|
||||
<string name="all_rooms_youre_in_will_be_shown_in_home">All rooms you’re in will be shown in Home.</string>
|
||||
|
||||
|
|
|
@ -53,4 +53,9 @@
|
|||
android:title="@string/preference_root_help_about"
|
||||
app:fragment="im.vector.app.features.settings.VectorSettingsHelpAboutFragment" />
|
||||
|
||||
<im.vector.app.core.preference.VectorPreference
|
||||
android:icon="@drawable/ic_settings_root_help_about"
|
||||
android:title="@string/preference_root_legals"
|
||||
app:fragment="im.vector.app.features.settings.legals.LegalsFragment" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue