Updated on 2026-08-14
This commit is contained in:
parent
25d005a9b9
commit
94a04d8280
13 changed files with 303 additions and 32 deletions
|
|
@ -8,30 +8,46 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias ValueChange<V> = (V?) -> Unit
|
||||
typealias SafeValueChange<V> = (V) -> Unit
|
||||
typealias ValueChanged<V> = (V?) -> Unit
|
||||
typealias SafeValueChanged<V> = (V) -> Unit
|
||||
|
||||
class KeyValue(val key: String, val value: Any)
|
||||
|
||||
class ViewState(
|
||||
var isHidden: Boolean = false,
|
||||
var backgroundColor: Int? = -1
|
||||
isVisible: Boolean? = null,
|
||||
bgColor: Int? = -1
|
||||
) {
|
||||
|
||||
var descriptionVisibility: Int = 0x00000008
|
||||
set(value) {
|
||||
field = value
|
||||
onDescriptionVisibilityChanged?.invoke(value)
|
||||
}
|
||||
class State<T>(
|
||||
stateValue: T,
|
||||
var onValueChanged: SafeValueChanged<T>? = null
|
||||
) {
|
||||
var value = stateValue
|
||||
set(value) {
|
||||
if (preventSameChanges && field == value) return
|
||||
|
||||
var onDescriptionVisibilityChanged: SafeValueChange<Int>? = null
|
||||
field = value
|
||||
onValueChanged?.invoke(value)
|
||||
}
|
||||
|
||||
internal var preventSameChanges = true
|
||||
}
|
||||
|
||||
var isVisibleState = State(isVisible)
|
||||
var backgroundColor = State(bgColor)
|
||||
var descriptionVisibility = State(0x00000008)
|
||||
|
||||
internal fun preventSameChanges(isPrevented: Boolean) {
|
||||
val states = listOf(isVisibleState, backgroundColor, descriptionVisibility)
|
||||
states.forEach { it.preventSameChanges = isPrevented }
|
||||
}
|
||||
}
|
||||
|
||||
interface ItemViewModel : PayloadHolder {
|
||||
val viewState: ViewState
|
||||
var data: Any?
|
||||
var defaultData: Any?
|
||||
var onDataUpdated: ValueChange<Any?>?
|
||||
var onDataUpdated: ValueChanged<Any?>?
|
||||
|
||||
fun updateDataByView(data: Any?)
|
||||
}
|
||||
|
|
@ -57,7 +73,7 @@ open class BaseItemViewModel(
|
|||
}
|
||||
|
||||
// Use it for handling data updates in View
|
||||
override var onDataUpdated: ValueChange<Any?>? = null
|
||||
override var onDataUpdated: ValueChanged<Any?>? = null
|
||||
|
||||
// When data updates directly it invokes onDataUpdated
|
||||
// return true = data will update
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.tangemtest.R
|
|||
import com.tangem.tangemtest._arch.structure.StringId
|
||||
import com.tangem.tangemtest._arch.structure.StringResId
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ViewState
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
|
||||
|
||||
|
|
@ -34,11 +35,22 @@ abstract class BaseViewWidget(
|
|||
private var defaultBackground: Drawable? = view.background
|
||||
|
||||
init {
|
||||
if (item.viewModel.viewState.isHidden) {
|
||||
view.visibility = View.GONE
|
||||
} else {
|
||||
setBackgroundColor(item.viewModel.viewState.backgroundColor)
|
||||
subscribeToViewStateChanges(item.viewModel.viewState)
|
||||
initViewState(item.viewModel.viewState)
|
||||
}
|
||||
|
||||
protected open fun subscribeToViewStateChanges(viewState: ViewState) {
|
||||
viewState.isVisibleState.onValueChanged = { state ->
|
||||
state?.let { view.visibility = if (it) View.VISIBLE else View.GONE }
|
||||
}
|
||||
viewState.backgroundColor.onValueChanged = { setBackgroundColor(it) }
|
||||
}
|
||||
|
||||
protected open fun initViewState(viewState: ViewState) {
|
||||
viewState.preventSameChanges(false)
|
||||
viewState.isVisibleState.value = viewState.isVisibleState.value
|
||||
if (viewState.backgroundColor.value != -1) setBackgroundColor(viewState.backgroundColor.value)
|
||||
viewState.preventSameChanges(true)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package com.tangem.tangemtest.commons.view
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.StringId
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.SafeValueChanged
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class MultiActionView<V : TextView>(
|
||||
stateList: MutableList<ButtonState>,
|
||||
val child: V
|
||||
) {
|
||||
|
||||
interface State {
|
||||
val id: Id
|
||||
|
||||
fun getAction(): StateButtonAction
|
||||
fun getResNameId(): Int
|
||||
}
|
||||
|
||||
var afterAction: SafeValueChanged<Id>? = null
|
||||
|
||||
var state: Id = DefaultId.default
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
|
||||
field = value
|
||||
Log.d(this, "state changed to: ${getKey(value)}")
|
||||
btnState = stateHolder[getKey(value)]
|
||||
}
|
||||
|
||||
init {
|
||||
child.setOnClickListener {
|
||||
val state = btnState ?: return@setOnClickListener
|
||||
|
||||
Log.d(this, "child handled OnClick for: ${getKey(state.id)}")
|
||||
state.getAction().invoke()
|
||||
afterAction?.invoke(state.id)
|
||||
}
|
||||
}
|
||||
|
||||
protected var btnState: State? = null
|
||||
set(value) {
|
||||
if (value == null) return
|
||||
|
||||
field = value
|
||||
Log.d(this, "btnState changed to: ${getKey(value.id)}")
|
||||
child.setText(value.getResNameId())
|
||||
}
|
||||
|
||||
protected val stateHolder: MutableMap<String, State> = stateList.associateBy { getKey(it.id) }.toMutableMap()
|
||||
|
||||
fun performAction(id: Id) {
|
||||
Log.d(this, "performAction ${getKey(id)}")
|
||||
state = id
|
||||
child.performClick()
|
||||
}
|
||||
|
||||
protected open fun getKey(id: Id): String {
|
||||
return when (id) {
|
||||
is StringId -> id.value
|
||||
else -> stringOf(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DefaultId : Id { default }
|
||||
|
||||
typealias StateButtonAction = () -> Unit
|
||||
|
||||
class ButtonState(
|
||||
override val id: Id,
|
||||
@StringRes val name: Int,
|
||||
private val action: StateButtonAction
|
||||
) : MultiActionView.State {
|
||||
|
||||
override fun getAction(): StateButtonAction = action
|
||||
|
||||
override fun getResNameId(): Int = name
|
||||
}
|
||||
|
|
@ -13,7 +13,9 @@ import com.tangem.tangemtest._arch.structure.abstraction.iterate
|
|||
import com.tangem.tangemtest.commons.performAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.tunnel.ViewScreen
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.ItemTypes
|
||||
import com.tangem.tasks.ScanEvent
|
||||
import com.tangem.tasks.TaskError
|
||||
import com.tangem.tasks.TaskEvent
|
||||
|
|
@ -73,7 +75,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
|
|||
|
||||
fun toggleDescriptionVisibility(state: Boolean) {
|
||||
ldItemList.value?.iterate {
|
||||
it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
|
||||
it.viewModel.viewState.descriptionVisibility.value = if (state) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +89,41 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
|
|||
itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) }
|
||||
keyList.forEach { itemsManager.payload.remove(it) }
|
||||
}
|
||||
|
||||
fun showFields(type: ActionType) {
|
||||
toggleFieldsVisibility(type, true)
|
||||
}
|
||||
|
||||
fun hideFields(type: ActionType) {
|
||||
toggleFieldsVisibility(type, false)
|
||||
}
|
||||
|
||||
private fun toggleFieldsVisibility(type: ActionType, show: Boolean) {
|
||||
val oftenUsed = getItemsForTogglingVisibilityState(type)
|
||||
val hidden = getItemIdsWhichWontShows(type)
|
||||
itemsManager.getItems().iterate {
|
||||
if (!hidden.contains(it.id)) {
|
||||
if (!oftenUsed.contains(it.id)) {
|
||||
it.viewModel.viewState.isVisibleState.value = show
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun getItemsForTogglingVisibilityState(type: ActionType): List<Id> {
|
||||
return when (type) {
|
||||
ActionType.Personalize -> ItemTypes().oftenUsedList
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getItemIdsWhichWontShows(type: ActionType): List<Id> {
|
||||
return when (type) {
|
||||
ActionType.Personalize -> ItemTypes().hiddenList
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Notifier(private val vm: ActionViewModel) {
|
||||
|
|
|
|||
|
|
@ -47,9 +47,14 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView {
|
|||
actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
|
||||
|
||||
initFab()
|
||||
createWidgets { subscribeToViewModelChanges() }
|
||||
createWidgets {
|
||||
widgetsWasCreated()
|
||||
subscribeToViewModelChanges()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun widgetsWasCreated() {}
|
||||
|
||||
protected open fun bindViews() {
|
||||
itemContainer = mainView.findViewById(R.id.ll_container)
|
||||
actionFab = mainView.findViewById(R.id.fab_action)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
|
||||
class ItemTypes {
|
||||
|
|
@ -36,11 +37,81 @@ class ItemTypes {
|
|||
CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Denomination.Denomination, Token.Decimal
|
||||
)
|
||||
|
||||
val hiddenList = mutableListOf(
|
||||
val hiddenList = mutableListOf<Id>(
|
||||
CardNumber.Series, CardNumber.BatchId, Pins.Pin3, SigningMethod.SignExternal,
|
||||
SignHashExProp.CryptoExKey, SignHashExProp.CheckPin3, SettingsMask.OneApdu,
|
||||
SettingsMask.UseBlock, SettingsMask.ProtectIssuerDataAgainstReplay,
|
||||
SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig
|
||||
)
|
||||
|
||||
}
|
||||
val oftenUsedList = listOf<Id>(
|
||||
// BlockId.CardNumber,
|
||||
BlockId.Common,
|
||||
// BlockId.SigningMethod,
|
||||
// BlockId.SignHashExProp,
|
||||
// BlockId.Denomination,
|
||||
// BlockId.Token,
|
||||
BlockId.ProdMask,
|
||||
// BlockId.SettingsMask,
|
||||
// BlockId.SettingsMaskProtocolEnc,
|
||||
BlockId.SettingsMaskNdef,
|
||||
// BlockId.Pins,
|
||||
Common.Curve,
|
||||
Common.Blockchain,
|
||||
Common.BlockchainCustom,
|
||||
// Common.MaxSignatures,
|
||||
Common.CreateWallet,
|
||||
// SigningMethod.SignTx,
|
||||
// SigningMethod.SignTxRaw,
|
||||
// SigningMethod.SignValidatedTx,
|
||||
// SigningMethod.SignValidatedTxRaw,
|
||||
// SigningMethod.SignValidatedTxIssuer,
|
||||
// SigningMethod.SignValidatedTxRawIssuer,
|
||||
// SigningMethod.SignExternal,
|
||||
// SignHashExProp.PinLessFloorLimit,
|
||||
// SignHashExProp.CryptoExKey,
|
||||
// SignHashExProp.RequireTerminalCertSig,
|
||||
// SignHashExProp.RequireTerminalTxSig,
|
||||
// SignHashExProp.CheckPin3,
|
||||
// Denomination.WriteOnPersonalize,
|
||||
// Denomination.Denomination,
|
||||
// Token.ItsToken,
|
||||
// Token.Symbol,
|
||||
// Token.ContractAddress,
|
||||
// Token.Decimal,
|
||||
ProductMask.Note,
|
||||
ProductMask.Tag,
|
||||
ProductMask.IdCard,
|
||||
ProductMask.IdIssuerCard,
|
||||
// SettingsMask.IsReusable,
|
||||
// SettingsMask.NeedActivation,
|
||||
// SettingsMask.ForbidPurge,
|
||||
// SettingsMask.AllowSelectBlockchain,
|
||||
// SettingsMask.UseBlock,
|
||||
// SettingsMask.OneApdu,
|
||||
// SettingsMask.UseCvc,
|
||||
// SettingsMask.AllowSwapPin,
|
||||
// SettingsMask.AllowSwapPin2,
|
||||
// SettingsMask.ForbidDefaultPin,
|
||||
// SettingsMask.SmartSecurityDelay,
|
||||
// SettingsMask.ProtectIssuerDataAgainstReplay,
|
||||
// SettingsMask.SkipSecurityDelayIfValidated,
|
||||
// SettingsMask.SkipPin2CvcIfValidated,
|
||||
// SettingsMask.SkipSecurityDelayOnLinkedTerminal,
|
||||
// SettingsMask.RestrictOverwriteExtraIssuerData,
|
||||
// SettingsMaskProtocolEnc.AllowUnencrypted,
|
||||
// SettingsMaskProtocolEnc.AllowStaticEncryption,
|
||||
// SettingsMaskNdef.UseNdef,
|
||||
// SettingsMaskNdef.DynamicNdef,
|
||||
// SettingsMaskNdef.DisablePrecomputedNdef,
|
||||
SettingsMaskNdef.Aar,
|
||||
SettingsMaskNdef.AarCustom,
|
||||
SettingsMaskNdef.Uri,
|
||||
// Pins.Pin,
|
||||
// Pins.Pin2,
|
||||
// Pins.Pin3,
|
||||
// Pins.Cvc,
|
||||
Pins.PauseBeforePin2
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
|
|||
blocList.add(pins())
|
||||
blocList.iterate {
|
||||
if (itemTypes.hiddenList.contains(it.id)) {
|
||||
it.viewModel.viewState.isHidden = true
|
||||
it.viewModel.viewState.isVisibleState.value = false
|
||||
}
|
||||
}
|
||||
return blocList
|
||||
|
|
|
|||
|
|
@ -2,19 +2,26 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.lifecycle.Observer
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.StringId
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.widget.WidgetBuilder
|
||||
import com.tangem.tangemtest.commons.view.ButtonState
|
||||
import com.tangem.tangemtest.commons.view.MultiActionView
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizationItemsManager
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.ItemError
|
||||
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizationItemBuilder
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
|
|
@ -31,6 +38,25 @@ class PersonalizationFragment : BaseCardActionFragment() {
|
|||
lifecycle.addObserver(itemsManager as PersonalizationItemsManager)
|
||||
}
|
||||
|
||||
override fun widgetsWasCreated() {
|
||||
super.widgetsWasCreated()
|
||||
|
||||
val btnContainer = itemContainer.inflate<ViewGroup>(R.layout.view_simple_button)
|
||||
val btn = btnContainer.findViewById<Button>(R.id.button)
|
||||
|
||||
val show = StringId("show")
|
||||
val hide = StringId("hide")
|
||||
val multiAction = MultiActionView(mutableListOf(
|
||||
ButtonState(show, R.string.show_rare_fields) { actionVM.showFields(ActionType.Personalize) },
|
||||
ButtonState(hide, R.string.hide_rare_fields) { actionVM.hideFields(ActionType.Personalize) }
|
||||
), btn)
|
||||
multiAction.afterAction = {
|
||||
multiAction.state = if (it == show) hide else show
|
||||
}
|
||||
multiAction.performAction(hide)
|
||||
itemContainer.addView(btnContainer)
|
||||
}
|
||||
|
||||
override fun initFab() {
|
||||
actionFab.setOnClickListener { actionVM.invokeMainAction() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.transition.AutoTransition
|
|||
import androidx.transition.TransitionManager
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ViewState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -19,13 +19,9 @@ abstract class DescriptionWidget(
|
|||
private val descriptionContainer: ViewGroup by lazy { view.findViewById<ViewGroup>(R.id.container_description) }
|
||||
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
|
||||
|
||||
init {
|
||||
Log.d(this, "init id: ${item.id}")
|
||||
initDescriptionWidget()
|
||||
}
|
||||
|
||||
private fun initDescriptionWidget() {
|
||||
item.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) }
|
||||
override fun subscribeToViewStateChanges(viewState: ViewState) {
|
||||
super.subscribeToViewStateChanges(viewState)
|
||||
viewState.descriptionVisibility.onValueChanged = { changeDescriptionVisibility(it) }
|
||||
}
|
||||
|
||||
protected open fun changeDescriptionVisibility(state: Int) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class ResponseViewModel : ViewModel() {
|
|||
|
||||
fun toggleDescriptionVisibility(state: Boolean) {
|
||||
itemList?.iterate {
|
||||
it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
|
||||
it.viewModel.viewState.descriptionVisibility.value = if (state) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class CardConverter : ModelToItems<Card> {
|
|||
else -> false
|
||||
}
|
||||
|
||||
if (isHidden) it.viewModel.viewState.isHidden = true
|
||||
if (isHidden) it.viewModel.viewState.isVisibleState.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +100,6 @@ class CardConverter : ModelToItems<Card> {
|
|||
|
||||
private fun createGroup(id: Id, colorId: Int? = null): ItemGroup {
|
||||
return if (colorId == null) SimpleItemGroup(id)
|
||||
else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(backgroundColor = colorId)))
|
||||
else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(bgColor = colorId)))
|
||||
}
|
||||
}
|
||||
20
tangem-demo/src/main/res/layout/view_simple_button.xml
Normal file
20
tangem-demo/src/main/res/layout/view_simple_button.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?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">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -18,4 +18,7 @@
|
|||
<string name="stub">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit</string>
|
||||
<!-- <string name="stub">Sed ut perspiciatis unde omnis iste natus</string>-->
|
||||
|
||||
<string name="show_rare_fields">Show rarely used fields</string>
|
||||
<string name="hide_rare_fields">Hide</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue