mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Login screens: Terms step for registration
This commit is contained in:
parent
dfbf448bb7
commit
3f80076fb1
17 changed files with 582 additions and 13 deletions
|
@ -27,5 +27,7 @@ interface RegistrationWizard {
|
|||
|
||||
fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
// TODO Add other method here
|
||||
}
|
||||
|
|
|
@ -40,5 +40,4 @@ sealed class Stage(open val mandatory: Boolean) {
|
|||
data class Other(override val mandatory: Boolean, val type: String, val params: Map<*, *>?) : Stage(mandatory)
|
||||
}
|
||||
|
||||
//TODO
|
||||
class TermPolicies
|
||||
typealias TermPolicies = Map<*, *>
|
||||
|
|
|
@ -28,6 +28,7 @@ import im.vector.matrix.android.api.util.NoOpCancellable
|
|||
import im.vector.matrix.android.internal.SessionManager
|
||||
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.util.CancelableCoroutine
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
|
@ -74,6 +75,21 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
|||
), callback)
|
||||
}
|
||||
|
||||
override fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
|
||||
return performRegistrationRequest(
|
||||
RegistrationParams(
|
||||
auth = AuthParams(
|
||||
type = LoginFlowTypes.TERMS,
|
||||
session = safeSession
|
||||
)
|
||||
), callback)
|
||||
}
|
||||
|
||||
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val result = runCatching {
|
||||
|
|
|
@ -84,7 +84,7 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
|||
LoginFlowTypes.RECAPTCHA -> Stage.ReCaptcha(isMandatory, ((params?.get(type) as? Map<*, *>)?.get("public_key") as? String)
|
||||
?: "")
|
||||
LoginFlowTypes.DUMMY -> Stage.Dummy
|
||||
LoginFlowTypes.TERMS -> Stage.Terms(isMandatory, TermPolicies())
|
||||
LoginFlowTypes.TERMS -> Stage.Terms(isMandatory, params?.get(type) as? TermPolicies ?: emptyMap<String, String>())
|
||||
LoginFlowTypes.EMAIL_IDENTITY -> Stage.Email(isMandatory)
|
||||
LoginFlowTypes.MSISDN -> Stage.Msisdn(isMandatory)
|
||||
else -> Stage.Other(isMandatory, type, (params?.get(type) as? Map<*, *>))
|
||||
|
|
|
@ -36,6 +36,7 @@ import im.vector.riotx.features.home.group.GroupListFragment
|
|||
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
|
||||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||
import im.vector.riotx.features.login.*
|
||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
||||
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
||||
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
|
||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
||||
|
@ -119,6 +120,11 @@ interface FragmentModule {
|
|||
@FragmentKey(LoginCaptchaFragment::class)
|
||||
fun bindLoginCaptchaFragment(fragment: LoginCaptchaFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LoginTermsFragment::class)
|
||||
fun bindLoginTermsFragment(fragment: LoginTermsFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LoginServerUrlFormFragment::class)
|
||||
|
|
|
@ -36,6 +36,7 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
data class AddMsisdn(val msisdn: String) : RegisterAction()
|
||||
data class ConfirmMsisdn(val code: String) : RegisterAction()
|
||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
||||
object AcceptTerms : RegisterAction()
|
||||
|
||||
// Reset actions
|
||||
open class ResetAction : LoginAction()
|
||||
|
|
|
@ -32,6 +32,9 @@ import im.vector.riotx.core.extensions.addFragmentToBackstack
|
|||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
||||
import im.vector.riotx.features.home.HomeActivity
|
||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
||||
import im.vector.riotx.features.login.terms.LoginTermsFragmentArgument
|
||||
import im.vector.riotx.features.login.terms.toLocalizedLoginTerms
|
||||
import kotlinx.android.synthetic.main.activity_login.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -184,21 +187,22 @@ class LoginActivity : VectorBaseActivity() {
|
|||
|
||||
private fun handleRegistrationNavigation(flowResult: FlowResult) {
|
||||
// Complete all mandatory stage first
|
||||
val mandatoryStages = flowResult.missingStages.filter { it.mandatory }
|
||||
val mandatoryStage = flowResult.missingStages.firstOrNull { it.mandatory }
|
||||
|
||||
if (mandatoryStages.isEmpty()) {
|
||||
if (mandatoryStage != null) {
|
||||
doStage(mandatoryStage)
|
||||
} else {
|
||||
// Consider optional stages
|
||||
val optionalStages = flowResult.missingStages.filter { !it.mandatory }
|
||||
if (optionalStages.isEmpty()) {
|
||||
val optionalStage = flowResult.missingStages.firstOrNull { !it.mandatory && it !is Stage.Dummy }
|
||||
if (optionalStage == null) {
|
||||
// Should not happen...
|
||||
} else {
|
||||
doStage(optionalStages.first())
|
||||
doStage(optionalStage)
|
||||
}
|
||||
} else {
|
||||
doStage(mandatoryStages.first())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Unstack fragment when stage is complete
|
||||
private fun doStage(stage: Stage) {
|
||||
when (stage) {
|
||||
is Stage.ReCaptcha -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
|
@ -210,12 +214,13 @@ class LoginActivity : VectorBaseActivity() {
|
|||
-> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
LoginGenericTextInputFormFragment::class.java,
|
||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory))
|
||||
is Stage.Terms
|
||||
-> TODO()
|
||||
is Stage.Terms -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
LoginTermsFragment::class.java,
|
||||
LoginTermsFragmentArgument(stage.policies.toLocalizedLoginTerms(getString(R.string.resources_language))))
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
|||
import im.vector.riotx.features.session.SessionListener
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* TODO To speed up registration, consider fetching registration flow instead of login flow at startup
|
||||
*/
|
||||
class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginViewState,
|
||||
private val authenticator: Authenticator,
|
||||
private val registrationService: RegistrationService,
|
||||
|
@ -100,10 +103,43 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
when (action) {
|
||||
is LoginAction.RegisterWith -> handleRegisterWith(action)
|
||||
is LoginAction.CaptchaDone -> handleCaptchaDone(action)
|
||||
is LoginAction.AcceptTerms -> handleAcceptTerms()
|
||||
// TODO Add other actions here
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAcceptTerms() {
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentTask = registrationWizard?.acceptTerms(object : MatrixCallback<RegistrationResult> {
|
||||
override fun onSuccess(data: RegistrationResult) {
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Success(data)
|
||||
)
|
||||
}
|
||||
|
||||
when (data) {
|
||||
is RegistrationResult.Success -> onSessionCreated(data.session)
|
||||
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO Handled JobCancellationException
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleRegisterWith(action: LoginAction.RegisterWith) {
|
||||
setState {
|
||||
copy(
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.login.terms
|
||||
|
||||
import org.matrix.androidsdk.rest.model.login.LocalizedFlowDataLoginTerms
|
||||
|
||||
data class LocalizedFlowDataLoginTermsChecked(val localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms,
|
||||
var checked: Boolean = false)
|
104
vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt
Executable file
104
vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt
Executable file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.login.terms
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
import butterknife.OnClick
|
||||
import com.airbnb.mvrx.args
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.utils.openUrlInExternalBrowser
|
||||
import im.vector.riotx.features.login.AbstractLoginFragment
|
||||
import im.vector.riotx.features.login.LoginAction
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_login_terms.*
|
||||
import org.matrix.androidsdk.rest.model.login.LocalizedFlowDataLoginTerms
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class LoginTermsFragmentArgument(
|
||||
val localizedFlowDataLoginTerms: List<LocalizedFlowDataLoginTerms>
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* LoginTermsFragment displays the list of policies the user has to accept
|
||||
*/
|
||||
class LoginTermsFragment @Inject constructor(private val policyController: PolicyController) : AbstractLoginFragment(),
|
||||
PolicyController.PolicyControllerListener {
|
||||
|
||||
private val params: LoginTermsFragmentArgument by args()
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_terms
|
||||
|
||||
private var loginTermsViewState: LoginTermsViewState = LoginTermsViewState(emptyList())
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
loginTermsPolicyList.setController(policyController)
|
||||
policyController.listener = this
|
||||
|
||||
val list = ArrayList<LocalizedFlowDataLoginTermsChecked>()
|
||||
|
||||
params.localizedFlowDataLoginTerms
|
||||
.forEach {
|
||||
list.add(LocalizedFlowDataLoginTermsChecked(it))
|
||||
}
|
||||
|
||||
loginTermsViewState = LoginTermsViewState(list)
|
||||
|
||||
renderState()
|
||||
}
|
||||
|
||||
private fun renderState() {
|
||||
policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked)
|
||||
|
||||
// Button is enabled only if all checkboxes are checked
|
||||
loginTermsSubmit.isEnabled = loginTermsViewState.allChecked()
|
||||
}
|
||||
|
||||
override fun setChecked(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms, isChecked: Boolean) {
|
||||
if (isChecked) {
|
||||
loginTermsViewState.check(localizedFlowDataLoginTerms)
|
||||
} else {
|
||||
loginTermsViewState.uncheck(localizedFlowDataLoginTerms)
|
||||
}
|
||||
|
||||
renderState()
|
||||
}
|
||||
|
||||
override fun openPolicy(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms) {
|
||||
openUrlInExternalBrowser(requireContext(), localizedFlowDataLoginTerms.localizedUrl!!)
|
||||
|
||||
// This code crashed, because user is not authenticated yet
|
||||
//val intent = VectorWebViewActivity.getIntent(requireContext(),
|
||||
// localizedFlowDataLoginTerms.localizedUrl!!,
|
||||
// localizedFlowDataLoginTerms.localizedName!!,
|
||||
// WebViewMode.DEFAULT)
|
||||
//startActivity(intent)
|
||||
}
|
||||
|
||||
@OnClick(R.id.loginTermsSubmit)
|
||||
internal fun submit() {
|
||||
loginViewModel.handle(LoginAction.AcceptTerms)
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
// No op
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.login.terms
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import org.matrix.androidsdk.rest.model.login.LocalizedFlowDataLoginTerms
|
||||
|
||||
data class LoginTermsViewState(
|
||||
val localizedFlowDataLoginTermsChecked: List<LocalizedFlowDataLoginTermsChecked>
|
||||
) : MvRxState {
|
||||
fun check(data: LocalizedFlowDataLoginTerms) {
|
||||
localizedFlowDataLoginTermsChecked.find { it.localizedFlowDataLoginTerms == data }?.checked = true
|
||||
}
|
||||
|
||||
fun uncheck(data: LocalizedFlowDataLoginTerms) {
|
||||
localizedFlowDataLoginTermsChecked.find { it.localizedFlowDataLoginTerms == data }?.checked = false
|
||||
}
|
||||
|
||||
fun allChecked(): Boolean {
|
||||
return localizedFlowDataLoginTermsChecked.all { it.checked }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.login.terms
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import org.matrix.androidsdk.rest.model.login.LocalizedFlowDataLoginTerms
|
||||
import javax.inject.Inject
|
||||
|
||||
class PolicyController @Inject constructor() : TypedEpoxyController<List<LocalizedFlowDataLoginTermsChecked>>() {
|
||||
|
||||
var listener: PolicyControllerListener? = null
|
||||
|
||||
override fun buildModels(data: List<LocalizedFlowDataLoginTermsChecked>) {
|
||||
data.forEach { entry ->
|
||||
policy {
|
||||
id(entry.localizedFlowDataLoginTerms.policyName)
|
||||
checked(entry.checked)
|
||||
title(entry.localizedFlowDataLoginTerms.localizedName!!)
|
||||
|
||||
clickListener(View.OnClickListener { listener?.openPolicy(entry.localizedFlowDataLoginTerms) })
|
||||
checkChangeListener { _, isChecked ->
|
||||
listener?.setChecked(entry.localizedFlowDataLoginTerms, isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PolicyControllerListener {
|
||||
fun setChecked(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms, isChecked: Boolean)
|
||||
fun openPolicy(localizedFlowDataLoginTerms: LocalizedFlowDataLoginTerms)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2018 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.riotx.features.login.terms
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_policy)
|
||||
abstract class PolicyModel : EpoxyModelWithHolder<PolicyModel.Holder>() {
|
||||
@EpoxyAttribute
|
||||
var checked: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var title: String? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var checkChangeListener: CompoundButton.OnCheckedChangeListener? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var clickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.let {
|
||||
it.checkbox.isChecked = checked
|
||||
it.checkbox.setOnCheckedChangeListener(checkChangeListener)
|
||||
it.title.text = title
|
||||
it.view.setOnClickListener(clickListener)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure checkbox behaves as expected (remove the listener)
|
||||
override fun unbind(holder: Holder) {
|
||||
super.unbind(holder)
|
||||
holder.checkbox.setOnCheckedChangeListener(null)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val checkbox by bind<CheckBox>(R.id.adapter_item_policy_checkbox)
|
||||
val title by bind<TextView>(R.id.adapter_item_policy_title)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2019 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.riotx.features.login.terms
|
||||
|
||||
data class UrlAndName(
|
||||
val url: String,
|
||||
val name: String
|
||||
)
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2019 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.riotx.features.login.terms
|
||||
|
||||
import im.vector.matrix.android.api.auth.registration.TermPolicies
|
||||
import org.matrix.androidsdk.rest.model.login.LocalizedFlowDataLoginTerms
|
||||
|
||||
/**
|
||||
* This method extract the policies from the login terms parameter, regarding the user language.
|
||||
* For each policy, if user language is not found, the default language is used and if not found, the first url and name are used (not predictable)
|
||||
*
|
||||
* Example of Data:
|
||||
* <pre>
|
||||
* "m.login.terms": {
|
||||
* "policies": {
|
||||
* "privacy_policy": {
|
||||
* "version": "1.0",
|
||||
* "en": {
|
||||
* "url": "http:\/\/matrix.org\/_matrix\/consent?v=1.0",
|
||||
* "name": "Terms and Conditions"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*</pre>
|
||||
*
|
||||
* @param userLanguage the user language
|
||||
* @param defaultLanguage the default language to use if the user language is not found for a policy in registrationFlowResponse
|
||||
*/
|
||||
fun TermPolicies.toLocalizedLoginTerms(userLanguage: String,
|
||||
defaultLanguage: String = "en"): List<LocalizedFlowDataLoginTerms> {
|
||||
val result = ArrayList<LocalizedFlowDataLoginTerms>()
|
||||
|
||||
val policies = get("policies")
|
||||
if (policies is Map<*, *>) {
|
||||
policies.keys.forEach { policyName ->
|
||||
val localizedFlowDataLoginTerms = LocalizedFlowDataLoginTerms()
|
||||
localizedFlowDataLoginTerms.policyName = policyName as String
|
||||
|
||||
val policy = policies[policyName]
|
||||
|
||||
// Enter this policy
|
||||
if (policy is Map<*, *>) {
|
||||
// Version
|
||||
localizedFlowDataLoginTerms.version = policy["version"] as String?
|
||||
|
||||
var userLanguageUrlAndName: UrlAndName? = null
|
||||
var defaultLanguageUrlAndName: UrlAndName? = null
|
||||
var firstUrlAndName: UrlAndName? = null
|
||||
|
||||
// Search for language
|
||||
policy.keys.forEach { policyKey ->
|
||||
when (policyKey) {
|
||||
"version" -> Unit // Ignore
|
||||
userLanguage -> {
|
||||
// We found the data for the user language
|
||||
userLanguageUrlAndName = extractUrlAndName(policy[policyKey])
|
||||
}
|
||||
defaultLanguage -> {
|
||||
// We found default language
|
||||
defaultLanguageUrlAndName = extractUrlAndName(policy[policyKey])
|
||||
}
|
||||
else -> {
|
||||
if (firstUrlAndName == null) {
|
||||
// Get at least some data
|
||||
firstUrlAndName = extractUrlAndName(policy[policyKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy found language data by priority
|
||||
when {
|
||||
userLanguageUrlAndName != null -> {
|
||||
localizedFlowDataLoginTerms.localizedUrl = userLanguageUrlAndName!!.url
|
||||
localizedFlowDataLoginTerms.localizedName = userLanguageUrlAndName!!.name
|
||||
}
|
||||
defaultLanguageUrlAndName != null -> {
|
||||
localizedFlowDataLoginTerms.localizedUrl = defaultLanguageUrlAndName!!.url
|
||||
localizedFlowDataLoginTerms.localizedName = defaultLanguageUrlAndName!!.name
|
||||
}
|
||||
firstUrlAndName != null -> {
|
||||
localizedFlowDataLoginTerms.localizedUrl = firstUrlAndName!!.url
|
||||
localizedFlowDataLoginTerms.localizedName = firstUrlAndName!!.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.add(localizedFlowDataLoginTerms)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun extractUrlAndName(policyData: Any?): UrlAndName? {
|
||||
if (policyData is Map<*, *>) {
|
||||
val url = policyData["url"] as String?
|
||||
val name = policyData["name"] as String?
|
||||
|
||||
if (url != null && name != null) {
|
||||
return UrlAndName(url, name)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
43
vector/src/main/res/layout/fragment_login_terms.xml
Normal file
43
vector/src/main/res/layout/fragment_login_terms.xml
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?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"
|
||||
style="@style/LoginContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImageView"
|
||||
style="@style/LoginTopIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginTermsNotice"
|
||||
style="@style/TextAppearance.Vector.Login.Text.Small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="27dp"
|
||||
android:text="@string/auth_accept_policies"
|
||||
app:layout_constraintTop_toBottomOf="@+id/logoImageView" />
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/loginTermsPolicyList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginTermsSubmit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginTermsNotice"
|
||||
tools:listitem="@layout/item_policy" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginTermsSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:text="@string/accept"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
48
vector/src/main/res/layout/item_policy.xml
Normal file
48
vector/src/main/res/layout/item_policy.xml
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?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:minHeight="48dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/adapter_item_policy_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/adapter_item_policy_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/adapter_item_policy_arrow"
|
||||
app:layout_constraintStart_toEndOf="@+id/adapter_item_policy_checkbox"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Policy title" />
|
||||
|
||||
<!-- Do not use drawableEnd on the TextView because of RTL support -->
|
||||
<ImageView
|
||||
android:id="@+id/adapter_item_policy_arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:rotationY="@integer/rtl_mirror_flip"
|
||||
android:src="@drawable/ic_material_chevron_right_black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue