Identity: cleanup Epoxy items

This commit is contained in:
Benoit Marty 2020-05-10 23:15:25 +02:00
parent ae0d09a049
commit bdfcf5c67c
22 changed files with 588 additions and 531 deletions

View File

@ -44,6 +44,8 @@ class DiscoverySettingsController @Inject constructor(
var listener: Listener? = null
private val codes = mutableMapOf<ThreePid, String>()
override fun buildModels(data: DiscoverySettingsState) {
when (data.identityServer) {
is Loading -> {
@ -59,9 +61,7 @@ class DiscoverySettingsController @Inject constructor(
}
is Success -> {
buildIdentityServerSection(data)
val hasIdentityServer = data.identityServer().isNullOrBlank().not()
if (hasIdentityServer) {
buildMailSection(data)
buildPhoneNumberSection(data)
@ -70,226 +70,17 @@ class DiscoverySettingsController @Inject constructor(
}
}
private fun buildPhoneNumberSection(data: DiscoverySettingsState) {
settingsSectionTitle {
id("msisdn")
titleResId(R.string.settings_discovery_msisdn_title)
}
when (data.phoneNumbersList) {
is Incomplete -> {
loadingItem {
id("msisdnLoading")
}
}
is Fail -> {
settingsInfoItem {
id("msisdnListError")
helperText(data.phoneNumbersList.error.message)
}
}
is Success -> {
val phones = data.phoneNumbersList.invoke()
if (phones.isEmpty()) {
settingsInfoItem {
id("no_msisdn")
helperText(stringProvider.getString(R.string.settings_discovery_no_msisdn))
}
} else {
phones.forEach { piState ->
val phoneNumber = try {
PhoneNumberUtil.getInstance().parse("+${piState.threePid.value}", null)
} catch (t: Throwable) {
Timber.e(t, "Unable to parse the phone number")
null
}
?.let {
PhoneNumberUtil.getInstance().format(it, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
}
settingsTextButtonItem {
id(piState.threePid.value)
title(phoneNumber)
colorProvider(colorProvider)
stringProvider(stringProvider)
when (piState.isShared) {
is Loading -> {
buttonIndeterminate(true)
}
is Fail -> {
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
buttonStyle(SettingsTextButtonItem.ButtonStyle.DESTRUCTIVE)
buttonTitle(stringProvider.getString(R.string.global_retry))
infoMessage(piState.isShared.error.message)
buttonClickListener { listener?.onTapRetryToRetrieveBindings() }
}
is Success -> when (piState.isShared()) {
SharedState.SHARED,
SharedState.NOT_SHARED -> {
checked(piState.isShared() == SharedState.SHARED)
buttonType(SettingsTextButtonItem.ButtonType.SWITCH)
switchChangeListener { _, checked ->
if (checked) {
listener?.onTapShare(piState.threePid)
} else {
listener?.onTapRevoke(piState.threePid)
}
}
}
SharedState.BINDING_IN_PROGRESS -> {
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
buttonTitle(null)
}
}
}
}
when (piState.isShared()) {
SharedState.BINDING_IN_PROGRESS -> {
val errorText = if (piState.finalRequest is Fail) {
val error = piState.finalRequest.error
// Deal with error 500
//Ref: https://github.com/matrix-org/sydent/issues/292
if (error is Failure.ServerError
&& error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) {
stringProvider.getString(R.string.settings_text_message_sent_wrong_code)
} else {
errorFormatter.toHumanReadable(error)
}
} else {
null
}
settingsItemEditText {
id("tverif" + piState.threePid.value)
descriptionText(stringProvider.getString(R.string.settings_text_message_sent, phoneNumber))
errorText(errorText)
inProgress(piState.finalRequest is Loading)
interactionListener(object : SettingsItemEditText.Listener {
override fun onValidate(code: String) {
if (piState.threePid is ThreePid.Msisdn) {
listener?.sendMsisdnVerificationCode(piState.threePid, code)
}
}
override fun onCancel() {
listener?.cancelBinding(piState.threePid)
}
})
}
}
else -> Unit
}.exhaustive
}
}
}
}
}
private fun buildMailSection(data: DiscoverySettingsState) {
settingsSectionTitle {
id("emails")
titleResId(R.string.settings_discovery_emails_title)
}
when (data.emailList) {
is Incomplete -> {
loadingItem {
id("mailLoading")
}
}
is Fail -> {
settingsInfoItem {
id("mailListError")
helperText(data.emailList.error.message)
}
}
is Success -> {
val emails = data.emailList.invoke()
if (emails.isEmpty()) {
settingsInfoItem {
id("no_emails")
helperText(stringProvider.getString(R.string.settings_discovery_no_mails))
}
} else {
emails.forEach { piState ->
settingsTextButtonItem {
id(piState.threePid.value)
title(piState.threePid.value)
colorProvider(colorProvider)
stringProvider(stringProvider)
when (piState.isShared) {
is Loading -> {
buttonIndeterminate(true)
showBottomButtons(false)
}
is Fail -> {
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
buttonStyle(SettingsTextButtonItem.ButtonStyle.DESTRUCTIVE)
buttonTitle(stringProvider.getString(R.string.global_retry))
infoMessage(piState.isShared.error.message)
buttonClickListener { listener?.onTapRetryToRetrieveBindings() }
showBottomButtons(false)
}
is Success -> when (piState.isShared()) {
SharedState.SHARED,
SharedState.NOT_SHARED -> {
checked(piState.isShared() == SharedState.SHARED)
buttonType(SettingsTextButtonItem.ButtonType.SWITCH)
switchChangeListener { _, checked ->
if (checked) {
listener?.onTapShare(piState.threePid)
} else {
listener?.onTapRevoke(piState.threePid)
}
}
}
SharedState.BINDING_IN_PROGRESS -> {
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
buttonTitle(null)
showBottomButtons(true)
when (piState.finalRequest) {
is Uninitialized -> {
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail, piState.threePid.value))
infoMessageTintColorId(R.color.vector_info_color)
showBottomLoading(false)
}
is Loading -> {
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail, piState.threePid.value))
infoMessageTintColorId(R.color.vector_info_color)
showBottomLoading(true)
}
is Fail -> {
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail_not_clicked, piState.threePid.value))
infoMessageTintColorId(R.color.riotx_destructive_accent)
showBottomLoading(false)
}
is Success -> Unit /* Cannot happen */
}
cancelClickListener { listener?.cancelBinding(piState.threePid) }
continueClickListener {
if (piState.threePid is ThreePid.Email) {
listener?.checkEmailVerification(piState.threePid)
}
}
}
}
}
}
}
}
}
}
}
private fun buildIdentityServerSection(data: DiscoverySettingsState) {
val identityServer = data.identityServer() ?: stringProvider.getString(R.string.none)
settingsSectionTitle {
settingsSectionTitleItem {
id("idsTitle")
titleResId(R.string.identity_server)
}
settingsItem {
id("idServer")
description(identityServer)
title(identityServer)
}
settingsInfoItem {
@ -316,7 +107,7 @@ class DiscoverySettingsController @Inject constructor(
} else {
buttonTitleId(R.string.add_identity_server)
}
buttonStyle(SettingsTextButtonItem.ButtonStyle.POSITIVE)
buttonStyle(SettingsTextButtonSingleLineItem.ButtonStyle.POSITIVE)
buttonClickListener(View.OnClickListener {
listener?.onTapChangeIdentityServer()
})
@ -331,7 +122,7 @@ class DiscoverySettingsController @Inject constructor(
id("remove")
colorProvider(colorProvider)
buttonTitleId(R.string.disconnect_identity_server)
buttonStyle(SettingsTextButtonItem.ButtonStyle.DESTRUCTIVE)
buttonStyle(SettingsTextButtonSingleLineItem.ButtonStyle.DESTRUCTIVE)
buttonClickListener(View.OnClickListener {
listener?.onTapDisconnectIdentityServer()
})
@ -339,6 +130,273 @@ class DiscoverySettingsController @Inject constructor(
}
}
private fun buildMailSection(data: DiscoverySettingsState) {
settingsSectionTitleItem {
id("emails")
titleResId(R.string.settings_discovery_emails_title)
}
when (data.emailList) {
is Incomplete -> {
loadingItem {
id("mailLoading")
}
}
is Fail -> {
settingsInfoItem {
id("mailListError")
helperText(data.emailList.error.message)
}
}
is Success -> {
val emails = data.emailList.invoke()
if (emails.isEmpty()) {
settingsInfoItem {
id("no_emails")
helperText(stringProvider.getString(R.string.settings_discovery_no_mails))
}
} else {
emails.forEach { buildEmail(it) }
}
}
}
}
private fun buildEmail(pidInfo: PidInfo) {
settingsTextButtonSingleLineItem {
id(pidInfo.threePid.value)
title(pidInfo.threePid.value)
colorProvider(colorProvider)
stringProvider(stringProvider)
when (pidInfo.isShared) {
is Loading -> {
buttonIndeterminate(true)
}
is Fail -> {
buttonStyle(SettingsTextButtonSingleLineItem.ButtonStyle.DESTRUCTIVE)
buttonTitle(stringProvider.getString(R.string.global_retry))
iconMode(IconMode.Error)
buttonClickListener { listener?.onTapRetryToRetrieveBindings() }
}
is Success -> when (pidInfo.isShared()) {
SharedState.SHARED,
SharedState.NOT_SHARED -> {
checked(pidInfo.isShared() == SharedState.SHARED)
buttonType(SettingsTextButtonSingleLineItem.ButtonType.SWITCH)
switchChangeListener { _, checked ->
if (checked) {
listener?.onTapShare(pidInfo.threePid)
} else {
listener?.onTapRevoke(pidInfo.threePid)
}
}
}
SharedState.BINDING_IN_PROGRESS -> {
buttonType(SettingsTextButtonSingleLineItem.ButtonType.NO_BUTTON)
when (pidInfo.finalRequest) {
is Incomplete -> iconMode(IconMode.Info)
is Fail -> iconMode(IconMode.Error)
else -> iconMode(IconMode.None)
}
}
}
}
}
if (pidInfo.isShared is Fail) {
settingsInformationItem {
id("info${pidInfo.threePid.value}")
colorProvider(colorProvider)
infoMessageColorId(R.color.vector_error_color)
infoMessage(pidInfo.isShared.error.message ?: "")
}
buildContinueCancel(pidInfo.threePid)
}
if (pidInfo.isShared() == SharedState.BINDING_IN_PROGRESS) {
when (pidInfo.finalRequest) {
is Uninitialized -> {
settingsInformationItem {
id("info${pidInfo.threePid.value}")
colorProvider(colorProvider)
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail, pidInfo.threePid.value))
infoMessageColorId(R.color.vector_info_color)
}
buildContinueCancel(pidInfo.threePid)
}
is Loading -> {
settingsInformationItem {
id("info${pidInfo.threePid.value}")
colorProvider(colorProvider)
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail, pidInfo.threePid.value))
infoMessageColorId(R.color.vector_info_color)
}
settingsProgressItem {
id("progress${pidInfo.threePid.value}")
}
}
is Fail -> {
settingsInformationItem {
id("info${pidInfo.threePid.value}")
colorProvider(colorProvider)
infoMessage(stringProvider.getString(R.string.settings_discovery_confirm_mail_not_clicked, pidInfo.threePid.value))
infoMessageColorId(R.color.riotx_destructive_accent)
}
buildContinueCancel(pidInfo.threePid)
}
is Success -> Unit /* Cannot happen */
}
}
}
private fun buildPhoneNumberSection(data: DiscoverySettingsState) {
settingsSectionTitleItem {
id("msisdn")
titleResId(R.string.settings_discovery_msisdn_title)
}
when (data.phoneNumbersList) {
is Incomplete -> {
loadingItem {
id("msisdnLoading")
}
}
is Fail -> {
settingsInfoItem {
id("msisdnListError")
helperText(data.phoneNumbersList.error.message)
}
}
is Success -> {
val phones = data.phoneNumbersList.invoke()
if (phones.isEmpty()) {
settingsInfoItem {
id("no_msisdn")
helperText(stringProvider.getString(R.string.settings_discovery_no_msisdn))
}
} else {
phones.forEach { buildMsisdn(it) }
}
}
}
}
private fun buildMsisdn(pidInfo: PidInfo) {
val phoneNumber = try {
PhoneNumberUtil.getInstance().parse("+${pidInfo.threePid.value}", null)
} catch (t: Throwable) {
Timber.e(t, "Unable to parse the phone number")
null
}
?.let {
PhoneNumberUtil.getInstance().format(it, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
}
?: pidInfo.threePid.value
settingsTextButtonSingleLineItem {
id(pidInfo.threePid.value)
title(phoneNumber)
colorProvider(colorProvider)
stringProvider(stringProvider)
when (pidInfo.isShared) {
is Loading -> {
buttonIndeterminate(true)
}
is Fail -> {
buttonType(SettingsTextButtonSingleLineItem.ButtonType.NORMAL)
buttonStyle(SettingsTextButtonSingleLineItem.ButtonStyle.DESTRUCTIVE)
buttonTitle(stringProvider.getString(R.string.global_retry))
iconMode(IconMode.Error)
buttonClickListener { listener?.onTapRetryToRetrieveBindings() }
}
is Success -> when (pidInfo.isShared()) {
SharedState.SHARED,
SharedState.NOT_SHARED -> {
checked(pidInfo.isShared() == SharedState.SHARED)
buttonType(SettingsTextButtonSingleLineItem.ButtonType.SWITCH)
switchChangeListener { _, checked ->
if (checked) {
listener?.onTapShare(pidInfo.threePid)
} else {
listener?.onTapRevoke(pidInfo.threePid)
}
}
}
SharedState.BINDING_IN_PROGRESS -> {
buttonType(SettingsTextButtonSingleLineItem.ButtonType.NO_BUTTON)
}
}
}
}
if (pidInfo.isShared is Fail) {
settingsInformationItem {
id("info${pidInfo.threePid.value}")
colorProvider(colorProvider)
infoMessageColorId(R.color.vector_error_color)
infoMessage(pidInfo.isShared.error.message ?: "")
}
}
when (pidInfo.isShared()) {
SharedState.BINDING_IN_PROGRESS -> {
val errorText = if (pidInfo.finalRequest is Fail) {
val error = pidInfo.finalRequest.error
// Deal with error 500
//Ref: https://github.com/matrix-org/sydent/issues/292
if (error is Failure.ServerError
&& error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) {
stringProvider.getString(R.string.settings_text_message_sent_wrong_code)
} else {
errorFormatter.toHumanReadable(error)
}
} else {
null
}
settingsEditTextItem {
id("msisdnVerification${pidInfo.threePid.value}")
descriptionText(stringProvider.getString(R.string.settings_text_message_sent, phoneNumber))
errorText(errorText)
inProgress(pidInfo.finalRequest is Loading)
interactionListener(object : SettingsEditTextItem.Listener {
override fun onValidate() {
val code = codes[pidInfo.threePid]
if (pidInfo.threePid is ThreePid.Msisdn && code != null) {
listener?.sendMsisdnVerificationCode(pidInfo.threePid, code)
}
}
override fun onCodeChange(code: String) {
codes[pidInfo.threePid] = code
}
})
}
buildContinueCancel(pidInfo.threePid)
}
else -> Unit
}.exhaustive
}
private fun buildContinueCancel(threePid: ThreePid) {
settingsContinueCancelItem {
id("bottom${threePid.value}")
interactionListener(object : SettingsContinueCancelItem.Listener {
override fun onContinue() {
when (threePid) {
is ThreePid.Email -> {
listener?.checkEmailVerification(threePid)
}
is ThreePid.Msisdn -> {
val code = codes[threePid]
if (code != null) {
listener?.sendMsisdnVerificationCode(threePid, code)
}
}
}
}
override fun onCancel() {
listener?.cancelBinding(threePid)
}
})
}
}
interface Listener {
fun onSelectIdentityServer()
fun onTapRevoke(threePid: ThreePid)

View File

@ -278,9 +278,11 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
}
private fun cancelBinding(action: DiscoverySettingsAction.CancelBinding) {
// TODO: remove the callback
identityService.cancelBindThreePid(action.threePid, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
changeThreePidState(action.threePid, Success(SharedState.NOT_SHARED))
changeThreePidSubmitState(action.threePid, Uninitialized)
}
override fun onFailure(failure: Throwable) {

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 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.discovery
enum class IconMode {
None,
Info,
Error
}

View File

@ -40,7 +40,7 @@ abstract class SettingsButtonItem : EpoxyModelWithHolder<SettingsButtonItem.Hold
var buttonTitleId: Int? = null
@EpoxyAttribute
var buttonStyle: SettingsTextButtonItem.ButtonStyle = SettingsTextButtonItem.ButtonStyle.POSITIVE
var buttonStyle: SettingsTextButtonSingleLineItem.ButtonStyle = SettingsTextButtonSingleLineItem.ButtonStyle.POSITIVE
@EpoxyAttribute
var buttonClickListener: View.OnClickListener? = null
@ -54,10 +54,10 @@ abstract class SettingsButtonItem : EpoxyModelWithHolder<SettingsButtonItem.Hold
}
when (buttonStyle) {
SettingsTextButtonItem.ButtonStyle.POSITIVE -> {
SettingsTextButtonSingleLineItem.ButtonStyle.POSITIVE -> {
holder.button.setTextColor(colorProvider.getColor(R.color.riotx_accent))
}
SettingsTextButtonItem.ButtonStyle.DESTRUCTIVE -> {
SettingsTextButtonSingleLineItem.ButtonStyle.DESTRUCTIVE -> {
holder.button.setTextColor(colorProvider.getColor(R.color.riotx_destructive_accent))
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 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.discovery
import android.widget.Button
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_settings_continue_cancel)
abstract class SettingsContinueCancelItem : EpoxyModelWithHolder<SettingsContinueCancelItem.Holder>() {
@EpoxyAttribute
var interactionListener: Listener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.cancelButton.setOnClickListener {
interactionListener?.onCancel()
}
holder.continueButton.setOnClickListener {
interactionListener?.onContinue()
}
}
class Holder : VectorEpoxyHolder() {
val cancelButton by bind<Button>(R.id.settings_item_cancel_button)
val continueButton by bind<Button>(R.id.settings_item_continue_button)
}
interface Listener {
fun onContinue()
fun onCancel()
}
}

View File

@ -15,13 +15,10 @@
*/
package im.vector.riotx.features.discovery
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder
@ -31,7 +28,7 @@ import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_settings_edit_text)
abstract class SettingsItemEditText : EpoxyModelWithHolder<SettingsItemEditText.Holder>() {
abstract class SettingsEditTextItem : EpoxyModelWithHolder<SettingsEditTextItem.Holder>() {
@EpoxyAttribute var descriptionText: String? = null
@EpoxyAttribute var errorText: String? = null
@ -44,14 +41,6 @@ abstract class SettingsItemEditText : EpoxyModelWithHolder<SettingsItemEditText.
super.bind(holder)
holder.textView.setTextOrHide(descriptionText)
holder.validateButton.setOnClickListener {
val code = holder.editText.text.toString()
interactionListener?.onValidate(code)
}
holder.cancelButton.setOnClickListener {
interactionListener?.onCancel()
}
holder.editText.isEnabled = !inProgress
@ -61,13 +50,12 @@ abstract class SettingsItemEditText : EpoxyModelWithHolder<SettingsItemEditText.
holder.textInputLayout.error = errorText
}
holder.validateButton.isInvisible = inProgress
holder.progress.isVisible = inProgress
holder.editText.setOnEditorActionListener { tv, actionId, _ ->
holder.editText.doOnTextChanged { code, _, _, _ ->
code?.let { interactionListener?.onCodeChange(it.toString()) }
}
holder.editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val code = tv.text.toString()
interactionListener?.onValidate(code)
interactionListener?.onValidate()
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
@ -75,16 +63,13 @@ abstract class SettingsItemEditText : EpoxyModelWithHolder<SettingsItemEditText.
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.settings_item_description)
val editText by bind<EditText>(R.id.settings_item_edittext)
val textInputLayout by bind<TextInputLayout>(R.id.settings_item_enter_til)
val validateButton by bind<Button>(R.id.settings_item_enter_button)
val cancelButton by bind<Button>(R.id.settings_item_enter_cancel_button)
val progress by bind<View>(R.id.settings_item_enter_progress)
val textView by bind<TextView>(R.id.settings_item_edit_text_description)
val editText by bind<EditText>(R.id.settings_item_edit_text)
val textInputLayout by bind<TextInputLayout>(R.id.settings_item_edit_text_til)
}
interface Listener {
fun onValidate(code: String)
fun onCancel()
fun onValidate()
fun onCodeChange(code: String)
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2020 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.discovery
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.view.isVisible
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
import im.vector.riotx.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_settings_radio_single_line)
abstract class SettingsImageItem : EpoxyModelWithHolder<SettingsImageItem.Holder>() {
@EpoxyAttribute
var title: String? = null
@EpoxyAttribute
@StringRes
var titleResId: Int? = null
@EpoxyAttribute
@DrawableRes
var endIconResourceId: Int = -1
@EpoxyAttribute
var itemClickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
if (titleResId != null) {
holder.textView.setText(titleResId!!)
} else {
holder.textView.setTextOrHide(title)
}
if (endIconResourceId != -1) {
holder.accessoryImage.setImageResource(endIconResourceId)
holder.accessoryImage.isVisible = true
} else {
holder.accessoryImage.isVisible = false
}
holder.view.setOnClickListener(itemClickListener)
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.settings_item_text)
val accessoryImage by bind<ImageView>(R.id.settings_item_image)
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 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.discovery
import android.widget.TextView
import androidx.annotation.ColorRes
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
import im.vector.riotx.core.resources.ColorProvider
@EpoxyModelClass(layout = R.layout.item_settings_information)
abstract class SettingsInformationItem : EpoxyModelWithHolder<SettingsInformationItem.Holder>() {
@EpoxyAttribute
lateinit var colorProvider: ColorProvider
// TODO Rename message
@EpoxyAttribute
lateinit var infoMessage: String
@EpoxyAttribute
@ColorRes
var infoMessageColorId: Int = R.color.vector_error_color
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = infoMessage
val errorColor = colorProvider.getColor(infoMessageColorId)
holder.textView.setTextColor(errorColor)
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.settings_item_information)
}
}

View File

@ -61,15 +61,6 @@ abstract class SettingsItem : EpoxyModelWithHolder<SettingsItem.Holder>() {
holder.descriptionText.setTextOrHide(description)
}
//If there is only a description, use primary color
// holder.descriptionText.setTextColor(
// if (holder.titleText.text.isNullOrBlank()) {
// ThemeUtils.getColor(holder.main.context, android.R.attr.textColorPrimary)
// } else {
// ThemeUtils.getColor(holder.main.context, android.R.attr.textColorSecondary)
// }
// )
holder.switchButton.isVisible = false
holder.view.setOnClickListener(itemClickListener)

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2020 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.discovery
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_settings_progress)
abstract class SettingsProgressItem : EpoxyModelWithHolder<SettingsProgressItem.Holder>() {
class Holder : VectorEpoxyHolder()
}

View File

@ -25,7 +25,7 @@ import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_settings_section_title)
abstract class SettingsSectionTitle : EpoxyModelWithHolder<SettingsSectionTitle.Holder>() {
abstract class SettingsSectionTitleItem : EpoxyModelWithHolder<SettingsSectionTitleItem.Holder>() {
@EpoxyAttribute
var title: String? = null

View File

@ -20,7 +20,6 @@ import android.widget.CompoundButton
import android.widget.ProgressBar
import android.widget.Switch
import android.widget.TextView
import androidx.annotation.ColorRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.view.isInvisible
@ -32,13 +31,14 @@ import im.vector.riotx.R
import im.vector.riotx.core.epoxy.ClickListener
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.onClick
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.themes.ThemeUtils
@EpoxyModelClass(layout = R.layout.item_settings_button_single_line)
abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonItem.Holder>() {
abstract class SettingsTextButtonSingleLineItem : EpoxyModelWithHolder<SettingsTextButtonSingleLineItem.Holder>() {
enum class ButtonStyle {
POSITIVE,
@ -46,6 +46,7 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
}
enum class ButtonType {
NO_BUTTON,
NORMAL,
SWITCH
}
@ -63,6 +64,9 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
@StringRes
var titleResId: Int? = null
@EpoxyAttribute
var iconMode: IconMode = IconMode.None
@EpoxyAttribute
var buttonTitle: String? = null
@ -82,35 +86,12 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
@EpoxyAttribute
var checked: Boolean? = null
@EpoxyAttribute
var showBottomLoading: Boolean = false
@EpoxyAttribute
var showBottomButtons: Boolean = false
@EpoxyAttribute
var buttonClickListener: ClickListener? = null
@EpoxyAttribute
var continueClickListener: ClickListener? = null
@EpoxyAttribute
var cancelClickListener: ClickListener? = null
@EpoxyAttribute
var switchChangeListener: CompoundButton.OnCheckedChangeListener? = null
@EpoxyAttribute
var infoMessage: String? = null
@EpoxyAttribute
@StringRes
var infoMessageId: Int? = null
@EpoxyAttribute
@ColorRes
var infoMessageTintColorId: Int = R.color.vector_error_color
override fun bind(holder: Holder) {
super.bind(holder)
@ -126,21 +107,19 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
holder.mainButton.setTextOrHide(buttonTitle)
}
holder.bottomLoading.isVisible = showBottomLoading
holder.continueButton.isInvisible = showBottomLoading || !showBottomButtons
holder.cancelButton.isVisible = !showBottomLoading && showBottomButtons
holder.continueButton.onClick(continueClickListener)
holder.cancelButton.onClick(cancelClickListener)
if (buttonIndeterminate) {
holder.spinner.isVisible = true
holder.progress.isVisible = true
holder.mainButton.isInvisible = true
holder.switchButton.isInvisible = true
holder.switchButton.setOnCheckedChangeListener(null)
holder.mainButton.setOnClickListener(null)
} else {
holder.spinner.isVisible = false
holder.progress.isVisible = false
when (buttonType) {
ButtonType.NO_BUTTON -> {
holder.mainButton.isVisible = false
holder.switchButton.isVisible = false
}
ButtonType.NORMAL -> {
holder.mainButton.isVisible = true
holder.switchButton.isVisible = false
@ -151,34 +130,39 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
ButtonStyle.DESTRUCTIVE -> {
holder.mainButton.setTextColor(colorProvider.getColor(R.color.vector_error_color))
}
}
}.exhaustive
holder.mainButton.onClick(buttonClickListener)
}
ButtonType.SWITCH -> {
holder.mainButton.isInvisible = true
holder.mainButton.isVisible = false
holder.switchButton.isVisible = true
//set to null before changing the state
holder.switchButton.setOnCheckedChangeListener(null)
checked?.let { holder.switchButton.isChecked = it }
holder.switchButton.setOnCheckedChangeListener(switchChangeListener)
}
}
}.exhaustive
}
val errorMessage = infoMessageId?.let { stringProvider.getString(it) } ?: infoMessage
if (errorMessage != null) {
holder.errorTextView.isVisible = true
holder.errorTextView.setTextOrHide(errorMessage)
val errorColor = colorProvider.getColor(infoMessageTintColorId)
when (iconMode) {
IconMode.None -> {
holder.textView.setCompoundDrawables(null, null, null, null)
}
IconMode.Info -> {
val errorColor = colorProvider.getColor(R.color.notification_accent_color)
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_notification_privacy_warning)?.apply {
ThemeUtils.tintDrawableWithColor(this, errorColor)
holder.textView.setCompoundDrawablesWithIntrinsicBounds(this, null, null, null)
}
holder.errorTextView.setTextColor(errorColor)
} else {
holder.errorTextView.isVisible = false
holder.errorTextView.text = null
holder.textView.setCompoundDrawables(null, null, null, null)
}
IconMode.Error -> {
val errorColor = colorProvider.getColor(R.color.vector_error_color)
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_notification_privacy_warning)?.apply {
ThemeUtils.tintDrawableWithColor(this, errorColor)
holder.textView.setCompoundDrawablesWithIntrinsicBounds(this, null, null, null)
}
}
}
}
@ -186,10 +170,6 @@ abstract class SettingsTextButtonItem : EpoxyModelWithHolder<SettingsTextButtonI
val textView by bind<TextView>(R.id.settings_item_text)
val mainButton by bind<Button>(R.id.settings_item_button)
val switchButton by bind<Switch>(R.id.settings_item_switch)
val spinner by bind<ProgressBar>(R.id.settings_item_button_spinner)
val errorTextView by bind<TextView>(R.id.settings_item_error_message)
val continueButton by bind<Button>(R.id.settings_item_continue_button)
val cancelButton by bind<Button>(R.id.settings_item_cancel_button)
val bottomLoading by bind<ProgressBar>(R.id.settings_item_bottom_loading)
val progress by bind<ProgressBar>(R.id.settings_item_progress)
}
}

View File

@ -18,7 +18,7 @@ package im.vector.riotx.features.terms
import android.view.View
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.riotx.R
import im.vector.riotx.features.discovery.settingsSectionTitle
import im.vector.riotx.features.discovery.settingsSectionTitleItem
import javax.inject.Inject
class TermsController @Inject constructor() : TypedEpoxyController<List<Term>>() {
@ -28,7 +28,7 @@ class TermsController @Inject constructor() : TypedEpoxyController<List<Term>>()
override fun buildModels(data: List<Term>?) {
data?.let {
settingsSectionTitle {
settingsSectionTitleItem {
id("header")
titleResId(R.string.widget_integration_review_terms)
}

View File

@ -3,8 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="?vctr_list_header_background_color"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="?riotx_background">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@ -2,8 +2,9 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:minHeight="64dp"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="?riotx_background"
android:minHeight="64dp">
<Button
android:id="@+id/settings_item_button"

View File

@ -4,9 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
android:foreground="?attr/selectableItemBackground"
android:orientation="vertical"
android:background="?riotx_background"
android:minHeight="68dp"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingTop="@dimen/layout_vertical_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
@ -16,17 +15,14 @@
android:id="@+id/settings_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:orientation="vertical"
android:textColor="?android:textColorPrimary"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="@+id/settings_item_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/settings_item_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/settings_item_button"
app:layout_constraintTop_toTopOf="parent"
tools:drawableLeft="@drawable/ic_notification_privacy_warning"
tools:drawableStart="@drawable/ic_notification_privacy_warning"
tools:drawableTint="@color/vector_error_color"
@ -37,23 +33,24 @@
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/share"
tools:text="@string/global_retry"
tools:visibility="visible" />
<ProgressBar
android:id="@+id/settings_item_button_spinner"
android:id="@+id/settings_item_progress"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/settings_item_button"
app:layout_constraintEnd_toEndOf="@+id/settings_item_button"
app:layout_constraintStart_toStartOf="@+id/settings_item_button"
app:layout_constraintTop_toTopOf="@+id/settings_item_button"
tools:visibility="invisible" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<Switch
android:id="@+id/settings_item_switch"
@ -61,55 +58,9 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/settings_item_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/settings_item_button"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/settings_item_error_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/settings_item_button"
tools:drawableStart="@drawable/ic_notification_privacy_warning"
tools:text="Error Message"
tools:textColor="@color/vector_info_color"
tools:visibility="visible">
</TextView>
<Button
android:id="@+id/settings_item_continue_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_item_error_message" />
<ProgressBar
android:id="@+id/settings_item_bottom_loading"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintBottom_toBottomOf="@+id/settings_item_continue_button"
app:layout_constraintEnd_toEndOf="@+id/settings_item_continue_button"
app:layout_constraintStart_toStartOf="@+id/settings_item_continue_button"
app:layout_constraintTop_toTopOf="@+id/settings_item_continue_button" />
<Button
android:id="@+id/settings_item_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:textColor="@color/riotx_destructive_accent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_item_error_message" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,29 @@
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin">
<Button
android:id="@+id/settings_item_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:textColor="@color/riotx_destructive_accent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/settings_item_continue_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,13 +5,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
android:orientation="vertical"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:paddingBottom="@dimen/layout_vertical_margin">
android:paddingEnd="@dimen/layout_horizontal_margin">
<TextView
android:id="@+id/settings_item_description"
android:id="@+id/settings_item_edit_text_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
@ -23,17 +21,17 @@
tools:text="@string/settings_text_message_sent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/settings_item_enter_til"
android:id="@+id/settings_item_edit_text_til"
style="@style/VectorTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_item_description">
app:layout_constraintTop_toBottomOf="@+id/settings_item_edit_text_description">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/settings_item_edittext"
android:id="@+id/settings_item_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone|flagNoPersonalizedLearning"
@ -44,33 +42,4 @@
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/settings_item_enter_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_item_enter_til" />
<Button
android:id="@+id/settings_item_enter_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/settings_item_enter_til" />
<ProgressBar
android:id="@+id/settings_item_enter_progress"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintBottom_toBottomOf="@+id/settings_item_enter_button"
app:layout_constraintEnd_toEndOf="@+id/settings_item_enter_button"
app:layout_constraintStart_toStartOf="@+id/settings_item_enter_button"
app:layout_constraintTop_toTopOf="@+id/settings_item_enter_button" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,25 @@
<?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="?riotx_background"
android:foreground="?attr/selectableItemBackground"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin">
<TextView
android:id="@+id/settings_item_information"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/settings_discovery_confirm_mail"
tools:textColor="@color/vector_info_color" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,21 @@
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:minHeight="48dp"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin">
<ProgressBar
android:id="@+id/settings_item_enter_progress"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="26dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
android:orientation="horizontal"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingTop="@dimen/layout_vertical_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:paddingBottom="@dimen/layout_vertical_margin">
<FrameLayout
android:layout_width="40dp"
android:layout_height="40dp">
<ImageView
android:id="@+id/settings_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:src="@drawable/unit_test" />
</FrameLayout>
<TextView
android:id="@+id/settings_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:orientation="vertical"
android:textColor="?android:textColorPrimary"
android:textSize="15sp"
tools:text="An option" />
</LinearLayout>

View File

@ -4,12 +4,12 @@
android:id="@+id/settings_section_title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_vertical_margin"
android:layout_marginBottom="@dimen/layout_vertical_margin"
android:orientation="vertical"
android:background="?vctr_list_header_background_color"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingTop="@dimen/layout_vertical_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:textColor="?android:textColorPrimary"
android:paddingBottom="@dimen/layout_vertical_margin"
android:textColor="?riotx_text_primary"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Title" />