mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-23 01:45:52 +03:00
Merge pull request #4184 from vector-im/feature/adm/is-policy
Always display identity server policies
This commit is contained in:
commit
d6af355335
18 changed files with 366 additions and 64 deletions
|
@ -40,13 +40,16 @@ fun Context.displayInWebView(url: String) {
|
|||
.show()
|
||||
}
|
||||
|
||||
fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, consentCallBack: (() -> Unit)) {
|
||||
fun Context.showIdentityServerConsentDialog(configuredIdentityServer: String?, policyLinkCallback: () -> Unit, consentCallBack: (() -> Unit)) {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.identity_server_consent_dialog_title)
|
||||
.setMessage(getString(R.string.identity_server_consent_dialog_content, configuredIdentityServer ?: ""))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
consentCallBack.invoke()
|
||||
}
|
||||
.setNeutralButton(R.string.identity_server_consent_dialog_neutral_policy) { _, _ ->
|
||||
policyLinkCallback.invoke()
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import im.vector.app.core.extensions.hideKeyboard
|
|||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.showIdentityServerConsentDialog
|
||||
import im.vector.app.databinding.FragmentContactsBookBinding
|
||||
import im.vector.app.features.navigation.SettingsActivityPayload
|
||||
import im.vector.app.features.userdirectory.PendingSelection
|
||||
import im.vector.app.features.userdirectory.UserListAction
|
||||
import im.vector.app.features.userdirectory.UserListSharedAction
|
||||
|
@ -74,9 +75,13 @@ class ContactsBookFragment @Inject constructor(
|
|||
private fun setupConsentView() {
|
||||
views.phoneBookSearchForMatrixContacts.setOnClickListener {
|
||||
withState(contactsBookViewModel) { state ->
|
||||
requireContext().showIdentityServerConsentDialog(state.identityServerUrl) {
|
||||
contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted)
|
||||
}
|
||||
requireContext().showIdentityServerConsentDialog(
|
||||
state.identityServerUrl,
|
||||
policyLinkCallback = {
|
||||
navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true))
|
||||
},
|
||||
consentCallBack = { contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.discovery
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_discovery_policy)
|
||||
abstract class DiscoveryPolicyItem : EpoxyModelWithHolder<DiscoveryPolicyItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var name: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var url: String? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var clickListener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.title.text = name
|
||||
holder.url.text = url
|
||||
holder.view.onClick(clickListener)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val title by bind<TextView>(R.id.discovery_policy_name)
|
||||
val url by bind<TextView>(R.id.discovery_policy_url)
|
||||
}
|
||||
}
|
|
@ -31,4 +31,5 @@ sealed class DiscoverySettingsAction : VectorViewModelAction {
|
|||
data class FinalizeBind3pid(val threePid: ThreePid) : DiscoverySettingsAction()
|
||||
data class SubmitMsisdnToken(val threePid: ThreePid.Msisdn, val code: String) : DiscoverySettingsAction()
|
||||
data class CancelBinding(val threePid: ThreePid) : DiscoverySettingsAction()
|
||||
data class SetPoliciesExpandState(val expanded: Boolean) : DiscoverySettingsAction()
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import im.vector.app.core.error.ErrorFormatter
|
|||
import im.vector.app.core.extensions.getFormattedValue
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.form.formAdvancedToggleItem
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.identity.SharedState
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
|
@ -62,7 +63,7 @@ class DiscoverySettingsController @Inject constructor(
|
|||
}
|
||||
is Success -> {
|
||||
buildIdentityServerSection(data)
|
||||
val hasIdentityServer = data.identityServer().isNullOrBlank().not()
|
||||
val hasIdentityServer = data.identityServer()?.serverUrl.isNullOrBlank().not()
|
||||
if (hasIdentityServer && !data.termsNotSigned) {
|
||||
buildConsentSection(data)
|
||||
buildEmailsSection(data.emailList)
|
||||
|
@ -106,7 +107,8 @@ class DiscoverySettingsController @Inject constructor(
|
|||
}
|
||||
|
||||
private fun buildIdentityServerSection(data: DiscoverySettingsState) {
|
||||
val identityServer = data.identityServer() ?: stringProvider.getString(R.string.none)
|
||||
val identityServer = data.identityServer()
|
||||
val identityServerUrl = identityServer?.serverUrl ?: stringProvider.getString(R.string.none)
|
||||
val host = this
|
||||
|
||||
settingsSectionTitleItem {
|
||||
|
@ -116,28 +118,58 @@ class DiscoverySettingsController @Inject constructor(
|
|||
|
||||
settingsItem {
|
||||
id("idServer")
|
||||
title(identityServer)
|
||||
title(identityServerUrl)
|
||||
}
|
||||
|
||||
if (data.identityServer() != null && data.termsNotSigned) {
|
||||
val policies = identityServer?.policies
|
||||
if (policies != null) {
|
||||
formAdvancedToggleItem {
|
||||
id("policy-urls")
|
||||
val titleRes = if (data.isIdentityPolicyUrlsExpanded) {
|
||||
R.string.settings_discovery_hide_identity_server_policy_title
|
||||
} else R.string.settings_discovery_show_identity_server_policy_title
|
||||
title(host.stringProvider.getString(titleRes))
|
||||
expanded(data.isIdentityPolicyUrlsExpanded)
|
||||
listener { host.listener?.onPolicyUrlsExpandedStateToggled(!data.isIdentityPolicyUrlsExpanded) }
|
||||
}
|
||||
if (data.isIdentityPolicyUrlsExpanded) {
|
||||
if (policies.isEmpty()) {
|
||||
settingsInfoItem {
|
||||
id("emptyPolicy")
|
||||
helperText(host.stringProvider.getString(R.string.settings_discovery_no_policy_provided))
|
||||
}
|
||||
} else {
|
||||
policies.forEach { policy ->
|
||||
discoveryPolicyItem {
|
||||
id(policy.url)
|
||||
name(policy.name)
|
||||
url(policy.url)
|
||||
clickListener { host.listener?.onPolicyTapped(policy) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (identityServer != null && data.termsNotSigned) {
|
||||
settingsInfoItem {
|
||||
id("idServerFooter")
|
||||
helperText(host.stringProvider.getString(R.string.settings_agree_to_terms, identityServer))
|
||||
helperText(host.stringProvider.getString(R.string.settings_agree_to_terms, identityServerUrl))
|
||||
showCompoundDrawable(true)
|
||||
itemClickListener { host.listener?.openIdentityServerTerms() }
|
||||
}
|
||||
settingsButtonItem {
|
||||
id("seeTerms")
|
||||
colorProvider(host.colorProvider)
|
||||
buttonTitle(host.stringProvider.getString(R.string.open_terms_of, identityServer))
|
||||
buttonTitle(host.stringProvider.getString(R.string.open_terms_of, identityServerUrl))
|
||||
buttonClickListener { host.listener?.openIdentityServerTerms() }
|
||||
}
|
||||
} else {
|
||||
settingsInfoItem {
|
||||
id("idServerFooter")
|
||||
showCompoundDrawable(false)
|
||||
if (data.identityServer() != null) {
|
||||
helperText(host.stringProvider.getString(R.string.settings_discovery_identity_server_info, identityServer))
|
||||
if (identityServer != null) {
|
||||
helperText(host.stringProvider.getString(R.string.settings_discovery_identity_server_info, identityServerUrl))
|
||||
} else {
|
||||
helperTextResId(R.string.settings_discovery_identity_server_info_none)
|
||||
}
|
||||
|
@ -147,7 +179,7 @@ class DiscoverySettingsController @Inject constructor(
|
|||
settingsButtonItem {
|
||||
id("change")
|
||||
colorProvider(host.colorProvider)
|
||||
if (data.identityServer() == null) {
|
||||
if (identityServer == null) {
|
||||
buttonTitleId(R.string.add_identity_server)
|
||||
} else {
|
||||
buttonTitleId(R.string.change_identity_server)
|
||||
|
@ -155,7 +187,7 @@ class DiscoverySettingsController @Inject constructor(
|
|||
buttonClickListener { host.listener?.onTapChangeIdentityServer() }
|
||||
}
|
||||
|
||||
if (data.identityServer() != null) {
|
||||
if (identityServer != null) {
|
||||
settingsInfoItem {
|
||||
id("removeInfo")
|
||||
helperTextResId(R.string.settings_discovery_disconnect_identity_server_info)
|
||||
|
@ -400,5 +432,7 @@ class DiscoverySettingsController @Inject constructor(
|
|||
fun onTapDisconnectIdentityServer()
|
||||
fun onTapUpdateUserConsent(newValue: Boolean)
|
||||
fun onTapRetryToRetrieveBindings()
|
||||
fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean)
|
||||
fun onPolicyTapped(policy: IdentityServerPolicy)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
@ -32,9 +33,11 @@ import im.vector.app.core.extensions.observeEvent
|
|||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.ensureProtocol
|
||||
import im.vector.app.core.utils.openUrlInChromeCustomTab
|
||||
import im.vector.app.core.utils.showIdentityServerConsentDialog
|
||||
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
||||
import im.vector.app.features.discovery.change.SetIdentityServerFragment
|
||||
import im.vector.app.features.navigation.SettingsActivityPayload
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import org.matrix.android.sdk.api.session.identity.SharedState
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
|
@ -52,6 +55,7 @@ class DiscoverySettingsFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private val viewModel by fragmentViewModel(DiscoverySettingsViewModel::class)
|
||||
private val discoveryArgs: SettingsActivityPayload.DiscoverySettings by args()
|
||||
|
||||
lateinit var sharedViewModel: DiscoverySharedViewModel
|
||||
|
||||
|
@ -77,6 +81,9 @@ class DiscoverySettingsFragment @Inject constructor(
|
|||
}
|
||||
}.exhaustive
|
||||
}
|
||||
if (discoveryArgs.expandIdentityPolicies) {
|
||||
viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -111,7 +118,7 @@ class DiscoverySettingsFragment @Inject constructor(
|
|||
requireContext(),
|
||||
termsActivityResultLauncher,
|
||||
TermsService.ServiceType.IdentityService,
|
||||
state.identityServer()?.ensureProtocol() ?: "",
|
||||
state.identityServer()?.serverUrl?.ensureProtocol() ?: "",
|
||||
null)
|
||||
}
|
||||
}
|
||||
|
@ -179,9 +186,11 @@ class DiscoverySettingsFragment @Inject constructor(
|
|||
override fun onTapUpdateUserConsent(newValue: Boolean) {
|
||||
if (newValue) {
|
||||
withState(viewModel) { state ->
|
||||
requireContext().showIdentityServerConsentDialog(state.identityServer.invoke()) {
|
||||
viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true))
|
||||
}
|
||||
requireContext().showIdentityServerConsentDialog(
|
||||
state.identityServer.invoke()?.serverUrl,
|
||||
policyLinkCallback = { viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true)) },
|
||||
consentCallBack = { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(false))
|
||||
|
@ -192,6 +201,14 @@ class DiscoverySettingsFragment @Inject constructor(
|
|||
viewModel.handle(DiscoverySettingsAction.RetrieveBinding)
|
||||
}
|
||||
|
||||
override fun onPolicyUrlsExpandedStateToggled(newExpandedState: Boolean) {
|
||||
viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = newExpandedState))
|
||||
}
|
||||
|
||||
override fun onPolicyTapped(policy: IdentityServerPolicy) {
|
||||
openUrlInChromeCustomTab(requireContext(), null, policy.url)
|
||||
}
|
||||
|
||||
private fun navigateToChangeIdentityServerFragment() {
|
||||
(vectorBaseActivity as? VectorSettingsActivity)?.navigateTo(SetIdentityServerFragment::class.java)
|
||||
}
|
||||
|
|
|
@ -21,10 +21,18 @@ import com.airbnb.mvrx.MvRxState
|
|||
import com.airbnb.mvrx.Uninitialized
|
||||
|
||||
data class DiscoverySettingsState(
|
||||
val identityServer: Async<String?> = Uninitialized,
|
||||
val identityServer: Async<IdentityServerWithTerms?> = Uninitialized,
|
||||
val emailList: Async<List<PidInfo>> = Uninitialized,
|
||||
val phoneNumbersList: Async<List<PidInfo>> = Uninitialized,
|
||||
// Can be true if terms are updated
|
||||
val termsNotSigned: Boolean = false,
|
||||
val userConsent: Boolean = false
|
||||
val userConsent: Boolean = false,
|
||||
val isIdentityPolicyUrlsExpanded: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
data class IdentityServerWithTerms(
|
||||
val serverUrl: String,
|
||||
val policies: List<IdentityServerPolicy>
|
||||
)
|
||||
|
||||
data class IdentityServerPolicy(val name: String, val url: String)
|
||||
|
|
|
@ -27,20 +27,25 @@ import com.airbnb.mvrx.ViewModelContext
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.R
|
||||
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.core.utils.ensureProtocol
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceListener
|
||||
import org.matrix.android.sdk.api.session.identity.SharedState
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
|
||||
class DiscoverySettingsViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: DiscoverySettingsState,
|
||||
private val session: Session) :
|
||||
VectorViewModel<DiscoverySettingsState, DiscoverySettingsAction, DiscoverySettingsViewEvents>(initialState) {
|
||||
private val session: Session,
|
||||
private val stringProvider: StringProvider
|
||||
) : VectorViewModel<DiscoverySettingsState, DiscoverySettingsAction, DiscoverySettingsViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
@ -57,25 +62,32 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private val identityService = session.identityService()
|
||||
private val termsService: TermsService = session
|
||||
|
||||
private val identityServerManagerListener = object : IdentityServiceListener {
|
||||
override fun onIdentityServerChange() = withState { state ->
|
||||
val identityServerUrl = identityService.getCurrentIdentityServerUrl()
|
||||
val currentIS = state.identityServer()
|
||||
setState {
|
||||
copy(
|
||||
identityServer = Success(identityServerUrl),
|
||||
userConsent = identityService.getUserConsent()
|
||||
viewModelScope.launch {
|
||||
runCatching { fetchIdentityServerWithTerms() }.fold(
|
||||
onSuccess = {
|
||||
val currentIS = state.identityServer()
|
||||
setState {
|
||||
copy(
|
||||
identityServer = Success(it),
|
||||
userConsent = identityService.getUserConsent()
|
||||
)
|
||||
}
|
||||
if (currentIS != it) retrieveBinding()
|
||||
},
|
||||
onFailure = { _viewEvents.post(DiscoverySettingsViewEvents.Failure(it)) }
|
||||
)
|
||||
}
|
||||
if (currentIS != identityServerUrl) retrieveBinding()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setState {
|
||||
copy(
|
||||
identityServer = Success(identityService.getCurrentIdentityServerUrl()),
|
||||
identityServer = Success(identityService.getCurrentIdentityServerUrl()?.let { IdentityServerWithTerms(it, emptyList()) }),
|
||||
userConsent = identityService.getUserConsent()
|
||||
)
|
||||
}
|
||||
|
@ -99,16 +111,17 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
|
||||
override fun handle(action: DiscoverySettingsAction) {
|
||||
when (action) {
|
||||
DiscoverySettingsAction.Refresh -> refreshPendingEmailBindings()
|
||||
DiscoverySettingsAction.RetrieveBinding -> retrieveBinding()
|
||||
DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer()
|
||||
is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action)
|
||||
is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action)
|
||||
is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action)
|
||||
is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action)
|
||||
is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true)
|
||||
is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action)
|
||||
is DiscoverySettingsAction.CancelBinding -> cancelBinding(action)
|
||||
DiscoverySettingsAction.Refresh -> fetchContent()
|
||||
DiscoverySettingsAction.RetrieveBinding -> retrieveBinding()
|
||||
DiscoverySettingsAction.DisconnectIdentityServer -> disconnectIdentityServer()
|
||||
is DiscoverySettingsAction.SetPoliciesExpandState -> updatePolicyUrlsExpandedState(action.expanded)
|
||||
is DiscoverySettingsAction.ChangeIdentityServer -> changeIdentityServer(action)
|
||||
is DiscoverySettingsAction.UpdateUserConsent -> handleUpdateUserConsent(action)
|
||||
is DiscoverySettingsAction.RevokeThreePid -> revokeThreePid(action)
|
||||
is DiscoverySettingsAction.ShareThreePid -> shareThreePid(action)
|
||||
is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true)
|
||||
is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action)
|
||||
is DiscoverySettingsAction.CancelBinding -> cancelBinding(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -135,6 +148,10 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updatePolicyUrlsExpandedState(isExpanded: Boolean) {
|
||||
setState { copy(isIdentityPolicyUrlsExpanded = isExpanded) }
|
||||
}
|
||||
|
||||
private fun changeIdentityServer(action: DiscoverySettingsAction.ChangeIdentityServer) {
|
||||
setState { copy(identityServer = Loading()) }
|
||||
|
||||
|
@ -143,7 +160,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
val data = session.identityService().setNewIdentityServer(action.url)
|
||||
setState {
|
||||
copy(
|
||||
identityServer = Success(data),
|
||||
identityServer = Success(IdentityServerWithTerms(data, emptyList())),
|
||||
userConsent = false
|
||||
)
|
||||
}
|
||||
|
@ -287,7 +304,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun retrieveBinding(threePids: List<ThreePid>) = withState { state ->
|
||||
if (state.identityServer().isNullOrBlank()) return@withState
|
||||
if (state.identityServer()?.serverUrl.isNullOrBlank()) return@withState
|
||||
|
||||
val emails = threePids.filterIsInstance<ThreePid.Email>()
|
||||
val msisdns = threePids.filterIsInstance<ThreePid.Msisdn>()
|
||||
|
@ -335,7 +352,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun submitMsisdnToken(action: DiscoverySettingsAction.SubmitMsisdnToken) = withState { state ->
|
||||
if (state.identityServer().isNullOrBlank()) return@withState
|
||||
if (state.identityServer()?.serverUrl.isNullOrBlank()) return@withState
|
||||
|
||||
changeThreePidSubmitState(action.threePid, Loading())
|
||||
|
||||
|
@ -378,12 +395,37 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun refreshPendingEmailBindings() = withState { state ->
|
||||
private fun fetchContent() = withState { state ->
|
||||
state.emailList()?.forEach { info ->
|
||||
when (info.isShared()) {
|
||||
SharedState.BINDING_IN_PROGRESS -> finalizeBind3pid(DiscoverySettingsAction.FinalizeBind3pid(info.threePid), false)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
runCatching { fetchIdentityServerWithTerms() }.fold(
|
||||
onSuccess = { setState { copy(identityServer = Success(it)) } },
|
||||
onFailure = { _viewEvents.post(DiscoverySettingsViewEvents.Failure(it)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchIdentityServerWithTerms(): IdentityServerWithTerms? {
|
||||
val identityServerUrl = identityService.getCurrentIdentityServerUrl()
|
||||
return identityServerUrl?.let {
|
||||
val terms = termsService.getTerms(TermsService.ServiceType.IdentityService, identityServerUrl.ensureProtocol())
|
||||
.serverResponse
|
||||
.getLocalizedTerms(stringProvider.getString(R.string.resources_language))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,6 +353,11 @@ class DefaultNavigator @Inject constructor(
|
|||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun openSettings(context: Context, payload: SettingsActivityPayload) {
|
||||
val intent = VectorSettingsActivity.getIntent(context, payload)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun openDebug(context: Context) {
|
||||
context.startActivity(Intent(context, DebugMenuActivity::class.java))
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ interface Navigator {
|
|||
|
||||
fun openSettings(context: Context, directAccess: Int = VectorSettingsActivity.EXTRA_DIRECT_ACCESS_ROOT)
|
||||
|
||||
fun openSettings(context: Context, payload: SettingsActivityPayload)
|
||||
|
||||
fun openDebug(context: Context)
|
||||
|
||||
fun openKeysBackupSetup(context: Context, showManualExport: Boolean)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.navigation
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed interface SettingsActivityPayload : Parcelable {
|
||||
|
||||
@Parcelize object Root : SettingsActivityPayload
|
||||
@Parcelize object AdvancedSettings : SettingsActivityPayload
|
||||
@Parcelize object SecurityPrivacy : SettingsActivityPayload
|
||||
@Parcelize object SecurityPrivacyManageSessions : SettingsActivityPayload
|
||||
@Parcelize object General : SettingsActivityPayload
|
||||
@Parcelize object Notifications : SettingsActivityPayload
|
||||
|
||||
@Parcelize
|
||||
data class DiscoverySettings(val expandIdentityPolicies: Boolean = false) : SettingsActivityPayload
|
||||
}
|
|
@ -44,6 +44,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
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_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY"
|
||||
const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY"
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
*/
|
||||
package im.vector.app.features.settings
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.preference.Preference
|
||||
|
@ -27,6 +31,7 @@ import im.vector.app.core.extensions.replaceFragment
|
|||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityVectorSettingsBinding
|
||||
import im.vector.app.features.discovery.DiscoverySettingsFragment
|
||||
import im.vector.app.features.navigation.SettingsActivityPayload
|
||||
import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment
|
||||
import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
|
||||
import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment
|
||||
|
@ -35,6 +40,8 @@ import org.matrix.android.sdk.api.session.Session
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_ACTIVITY_PAYLOAD = "settings-activity-payload"
|
||||
|
||||
/**
|
||||
* Displays the client settings.
|
||||
*/
|
||||
|
@ -64,27 +71,28 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
|
|||
|
||||
if (isFirstCreation()) {
|
||||
// display the fragment
|
||||
when (intent.getIntExtra(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOT)) {
|
||||
EXTRA_DIRECT_ACCESS_GENERAL ->
|
||||
|
||||
when (val payload = readPayload<SettingsActivityPayload>(SettingsActivityPayload.Root)) {
|
||||
SettingsActivityPayload.General ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsGeneralFragment::class.java, null, FRAGMENT_TAG)
|
||||
EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS ->
|
||||
SettingsActivityPayload.AdvancedSettings ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsAdvancedSettingsFragment::class.java, null, FRAGMENT_TAG)
|
||||
EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY ->
|
||||
SettingsActivityPayload.SecurityPrivacy ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsSecurityPrivacyFragment::class.java, null, FRAGMENT_TAG)
|
||||
EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS ->
|
||||
SettingsActivityPayload.SecurityPrivacyManageSessions ->
|
||||
replaceFragment(R.id.vector_settings_page,
|
||||
VectorSettingsDevicesFragment::class.java,
|
||||
null,
|
||||
FRAGMENT_TAG)
|
||||
EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> {
|
||||
SettingsActivityPayload.Notifications -> {
|
||||
requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG)
|
||||
}
|
||||
EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> {
|
||||
replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, null, FRAGMENT_TAG)
|
||||
is SettingsActivityPayload.DiscoverySettings -> {
|
||||
Log.e("!!!", "SettingsActivityPayload.DiscoverySettings : $payload")
|
||||
replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, payload, FRAGMENT_TAG)
|
||||
}
|
||||
|
||||
else ->
|
||||
else ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
@ -148,19 +156,31 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
|
|||
}
|
||||
}
|
||||
|
||||
fun <T : Fragment> navigateTo(fragmentClass: Class<T>) {
|
||||
fun <T : Fragment> navigateTo(fragmentClass: Class<T>, arguments: Bundle? = null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out)
|
||||
.replace(R.id.vector_settings_page, fragmentClass, null)
|
||||
.replace(R.id.vector_settings_page, fragmentClass, arguments)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context, directAccess: Int) = Intent(context, VectorSettingsActivity::class.java)
|
||||
.apply { putExtra(EXTRA_DIRECT_ACCESS, directAccess) }
|
||||
fun getIntent(context: Context, directAccess: Int) = Companion.getIntent(context, when (directAccess) {
|
||||
EXTRA_DIRECT_ACCESS_ROOT -> SettingsActivityPayload.Root
|
||||
EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS -> SettingsActivityPayload.AdvancedSettings
|
||||
EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY -> SettingsActivityPayload.SecurityPrivacy
|
||||
EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS -> SettingsActivityPayload.SecurityPrivacyManageSessions
|
||||
EXTRA_DIRECT_ACCESS_GENERAL -> SettingsActivityPayload.General
|
||||
EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> SettingsActivityPayload.Notifications
|
||||
EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> SettingsActivityPayload.DiscoverySettings()
|
||||
else -> {
|
||||
Timber.w("Unknown directAccess: $directAccess defaulting to Root")
|
||||
SettingsActivityPayload.Root
|
||||
}
|
||||
})
|
||||
|
||||
private const val EXTRA_DIRECT_ACCESS = "EXTRA_DIRECT_ACCESS"
|
||||
fun getIntent(context: Context, payload: SettingsActivityPayload) = Intent(context, VectorSettingsActivity::class.java)
|
||||
.applyPayload(payload)
|
||||
|
||||
const val EXTRA_DIRECT_ACCESS_ROOT = 0
|
||||
const val EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS = 1
|
||||
|
@ -173,3 +193,11 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
|
|||
private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment"
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Parcelable> Activity.readPayload(default: T): T {
|
||||
return intent.getParcelableExtra(KEY_ACTIVITY_PAYLOAD) ?: default
|
||||
}
|
||||
|
||||
private fun <T : Parcelable> Intent.applyPayload(payload: T): Intent {
|
||||
return putExtra(KEY_ACTIVITY_PAYLOAD, payload)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.app.R
|
|||
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
||||
import im.vector.app.core.extensions.hideKeyboard
|
||||
import im.vector.app.core.extensions.hidePassword
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import im.vector.app.core.intent.getFilenameFromUri
|
||||
import im.vector.app.core.platform.SimpleTextWatcher
|
||||
import im.vector.app.core.preference.UserAvatarPreference
|
||||
|
@ -50,6 +51,8 @@ import im.vector.app.core.utils.toast
|
|||
import im.vector.app.databinding.DialogChangePasswordBinding
|
||||
import im.vector.app.features.MainActivity
|
||||
import im.vector.app.features.MainActivityArgs
|
||||
import im.vector.app.features.discovery.DiscoverySettingsFragment
|
||||
import im.vector.app.features.navigation.SettingsActivityPayload
|
||||
import im.vector.app.features.workers.signout.SignOutUiWorker
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -173,6 +176,15 @@ class VectorSettingsGeneralFragment @Inject constructor(
|
|||
mPasswordPreference.isVisible = false
|
||||
}
|
||||
|
||||
val discoveryPreference = findPreference<VectorPreference>(VectorPreferences.SETTINGS_DISCOVERY_PREFERENCE_KEY)!!
|
||||
discoveryPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
(requireActivity() as VectorSettingsActivity).navigateTo(
|
||||
DiscoverySettingsFragment::class.java,
|
||||
SettingsActivityPayload.DiscoverySettings().toMvRxBundle()
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
// Advanced settings
|
||||
|
||||
// user account
|
||||
|
|
|
@ -43,6 +43,7 @@ import im.vector.app.core.utils.showIdentityServerConsentDialog
|
|||
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||
import im.vector.app.databinding.FragmentUserListBinding
|
||||
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
||||
import im.vector.app.features.navigation.SettingsActivityPayload
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.user.model.User
|
||||
|
@ -227,9 +228,13 @@ class UserListFragment @Inject constructor(
|
|||
|
||||
override fun giveIdentityServerConsent() {
|
||||
withState(viewModel) { state ->
|
||||
requireContext().showIdentityServerConsentDialog(state.configuredIdentityServer) {
|
||||
viewModel.handle(UserListAction.UpdateUserConsent(true))
|
||||
}
|
||||
requireContext().showIdentityServerConsentDialog(
|
||||
state.configuredIdentityServer,
|
||||
policyLinkCallback = {
|
||||
navigator.openSettings(requireContext(), SettingsActivityPayload.DiscoverySettings(expandIdentityPolicies = true))
|
||||
},
|
||||
consentCallBack = { viewModel.handle(UserListAction.UpdateUserConsent(true)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
vector/src/main/res/layout/item_discovery_policy.xml
Normal file
51
vector/src/main/res/layout/item_discovery_policy.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/discovery_policy_name"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Integration manager" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/discovery_policy_url"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
app:layout_constraintEnd_toStartOf="@id/discovery_policy_arrow"
|
||||
app:layout_constraintStart_toStartOf="@+id/discovery_policy_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/discovery_policy_name"
|
||||
tools:text="Use bots, bridges, widget and sticker packs." />
|
||||
|
||||
<!-- Do not use drawableEnd on the TextView because of RTL support -->
|
||||
<ImageView
|
||||
android:id="@+id/discovery_policy_arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:rotationY="@integer/rtl_mirror_flip"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?vctr_content_primary"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2349,6 +2349,9 @@
|
|||
<string name="add_identity_server">Configure identity server</string>
|
||||
<string name="open_discovery_settings">Open Discovery Settings</string>
|
||||
<string name="change_identity_server">Change identity server</string>
|
||||
<string name="settings_discovery_show_identity_server_policy_title">Show identity server policy</string>
|
||||
<string name="settings_discovery_hide_identity_server_policy_title">Hide identity server policy</string>
|
||||
<string name="settings_discovery_no_policy_provided">No policy provided by the identity server</string>
|
||||
<string name="settings_discovery_identity_server_info">You are currently using %1$s to discover and be discoverable by existing contacts you know.</string>
|
||||
<string name="settings_discovery_identity_server_info_none">You are not currently using an identity server. To discover and be discoverable by existing contacts you know, configure one below.</string>
|
||||
<string name="settings_discovery_emails_title">Discoverable email addresses</string>
|
||||
|
@ -2367,6 +2370,7 @@
|
|||
|
||||
<string name="identity_server_consent_dialog_title">Send emails and phone numbers</string>
|
||||
<string name="identity_server_consent_dialog_content">In order to discover existing contacts you know, do you accept to send your contact data (phone numbers and/or emails) to the configured identity server (%1$s)?\n\nFor more privacy, the sent data will be hashed before being sent.</string>
|
||||
<string name="identity_server_consent_dialog_neutral_policy">Policy</string>
|
||||
|
||||
<string name="settings_discovery_enter_identity_server">Enter an identity server URL</string>
|
||||
<string name="settings_discovery_bad_identity_server">Could not connect to identity server</string>
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
app:fragment="im.vector.app.features.settings.threepids.ThreePidsSettingsFragment" />
|
||||
|
||||
<im.vector.app.core.preference.VectorPreference
|
||||
android:key="SETTINGS_DISCOVERY_PREFERENCE_KEY"
|
||||
android:persistent="false"
|
||||
android:summary="@string/settings_discovery_manage"
|
||||
android:title="@string/settings_discovery_category"
|
||||
app:fragment="im.vector.app.features.discovery.DiscoverySettingsFragment" />
|
||||
android:title="@string/settings_discovery_category" />
|
||||
|
||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||
|
||||
|
|
Loading…
Reference in a new issue