Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-10 13:21:50 +03:00
commit f3fc2a80d1
100 changed files with 2121 additions and 1401 deletions

4
.gitignore vendored
View file

@ -13,6 +13,10 @@ local.properties
.idea/caches/ .idea/caches/
.idea/libraries/ .idea/libraries/
.idea/*.xml .idea/*.xml
.idea/codeStyles
# LayoutInspector files
.captures
# OS-specific files # OS-specific files
.DS_Store .DS_Store

View file

@ -1,116 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View file

@ -1,6 +1,8 @@
package com.tangem.tangemtest package com.tangem.tangemtest
import android.app.Application import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import com.tangem.tangemtest._arch.structure.ILog import com.tangem.tangemtest._arch.structure.ILog
import com.tangem.tangemtest._arch.structure.ItemLogger import com.tangem.tangemtest._arch.structure.ItemLogger
import com.tangem.tangemtest.commons.TangemLogger import com.tangem.tangemtest.commons.TangemLogger
@ -23,6 +25,10 @@ class AppTangemDemo : Application() {
ILog.setLogger(ItemLogger()) ILog.setLogger(ItemLogger())
} }
fun sharedPreferences(name: String = "DevKitApp", mode: Int = Context.MODE_PRIVATE): SharedPreferences {
return getSharedPreferences(name, mode)
}
companion object { companion object {
lateinit var appInstance: AppTangemDemo lateinit var appInstance: AppTangemDemo
} }

View file

@ -3,10 +3,6 @@ package com.tangem.tangemtest._arch.structure
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface DataHolder<D> {
var viewModel: D
}
typealias Payload = MutableMap<String, Any?> typealias Payload = MutableMap<String, Any?>
interface PayloadHolder { interface PayloadHolder {
@ -18,11 +14,3 @@ interface PayloadHolder {
payload[key] = value payload[key] = value
} }
} }
interface ItemListHolder<I> {
fun setItems(list: MutableList<I>)
fun getItems(): MutableList<I>
fun addItem(item: I)
fun removeItem(item: I)
fun clear()
}

View file

@ -5,6 +5,10 @@ package com.tangem.tangemtest._arch.structure
*/ */
interface Id interface Id
class StringId(val value: String) : Id
class StringResId(val value: Int) : Id
enum class Additional : Id { enum class Additional : Id {
UNDEFINED, UNDEFINED,
JSON_INCOMING, JSON_INCOMING,

View file

@ -5,11 +5,11 @@ import com.tangem.tangemtest._arch.structure.Id
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? { fun List<Item>.findItem(id: Id): Item? {
var foundItem: BaseItem<Any?>? = null var foundItem: Item? = null
iterate { iterate {
if (it.id == id) { if (it.id == id) {
foundItem = it as? BaseItem<Any?> foundItem = it
return@iterate return@iterate
} }
} }
@ -19,8 +19,8 @@ fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
fun List<Item>.iterate(func: (Item) -> Unit) { fun List<Item>.iterate(func: (Item) -> Unit) {
forEach { forEach {
when (it) { when (it) {
is BaseItem<*> -> func(it) is BaseItem -> func(it)
is Block -> it.itemList.iterate(func) is ItemGroup -> it.itemList.iterate(func)
} }
} }
} }

View file

@ -1,27 +1,26 @@
package com.tangem.tangemtest._arch.structure.abstraction package com.tangem.tangemtest._arch.structure.abstraction
import com.tangem.tangemtest._arch.structure.DataHolder
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.PayloadHolder
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface Item : PayloadHolder { interface Item {
val id: Id val id: Id
var parent: Item? var parent: Item?
var viewModel: ItemViewModel
fun added(parent: Item) {
this.parent = parent
} }
abstract class BaseItem<D>( fun removed(parent: Item) {
override var viewModel: ItemViewModel<D> this.parent = null
) : Item, DataHolder<ItemViewModel<D>> { }
override var parent: Item? = null
override val payload: Payload = mutableMapOf()
fun getData(): D? = viewModel.data fun <D> getData(): D? = viewModel.data as? D
fun setData(value: D?) { fun setData(value: Any?) {
viewModel.data = value viewModel.data = value
} }
@ -29,3 +28,12 @@ abstract class BaseItem<D>(
setData(viewModel.defaultData) setData(viewModel.defaultData)
} }
} }
open class BaseItem(
override val id: Id,
override var viewModel: ItemViewModel
) : Item {
override var parent: Item? = null
}

View file

@ -2,45 +2,47 @@ package com.tangem.tangemtest._arch.structure.abstraction
import com.tangem.tangemtest._arch.structure.ILog import com.tangem.tangemtest._arch.structure.ILog
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.ItemListHolder
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface Block : Item { interface ItemGroup : Item {
val itemList: MutableList<Item> val itemList: MutableList<Item>
fun setItems(list: MutableList<Item>)
fun getItems(): MutableList<Item>
fun addItem(item: Item)
fun removeItem(item: Item)
fun clear()
} }
abstract class BaseBlock : Block { open class SimpleItemGroup(
override val id: Id,
override var viewModel: ItemViewModel = BaseItemViewModel()
) : ItemGroup {
override var parent: Item? = null override var parent: Item? = null
override val itemList: MutableList<Item> = mutableListOf() override val itemList: MutableList<Item> = mutableListOf()
override val payload: MutableMap<String, Any?> = mutableMapOf()
}
open class ListItemBlock(
override val id: Id
) : BaseBlock(), ItemListHolder<Item> {
override fun setItems(list: MutableList<Item>) { override fun setItems(list: MutableList<Item>) {
ILog.d(this, "setItems into: $id, count: ${list.size}") ILog.d(this, "setItems into: $id, count: ${list.size}")
itemList.forEach { it.removed(this) }
itemList.clear() itemList.clear()
itemList.addAll(list) list.forEach { addItem(it) }
itemList.forEach { it.parent = this }
} }
override fun getItems(): MutableList<Item> { override fun getItems(): MutableList<Item> = itemList
return itemList
}
override fun addItem(item: Item) { override fun addItem(item: Item) {
ILog.d(this, "addItem into: $id, who: ${item.id}") ILog.d(this, "addItem into: $id, who: ${item.id}")
item.parent = this
itemList.add(item) itemList.add(item)
item.added(this)
} }
override fun removeItem(item: Item) { override fun removeItem(item: Item) {
ILog.d(this, "removeItem from: $id, which: ${item.id}") ILog.d(this, "removeItem from: $id, which: ${item.id}")
itemList.remove(item) itemList.remove(item)
item.removed(this)
} }
override fun clear() { override fun clear() {

View file

@ -11,57 +11,12 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder
typealias ValueChange<V> = (V?) -> Unit typealias ValueChange<V> = (V?) -> Unit
typealias SafeValueChange<V> = (V) -> Unit typealias SafeValueChange<V> = (V) -> Unit
interface ItemViewModel<D : Any?> : PayloadHolder { class KeyValue(val key: String, val value: Any)
val viewState: ViewState
var data: D?
var defaultData: D?
var onDataUpdated: ValueChange<D>?
fun updateDataByView(data: D?) class ViewState(
} var isHidden: Boolean = false,
var backgroundColor: Int? = -1
open class BaseItemViewModel<D> : ItemViewModel<D> { ) {
override val viewState: ViewState = ViewState()
override val payload: Payload = mutableMapOf()
// Don't update it directly from a View. Use for it updateDataByView()
override var data: D? = null
set(value) {
if (handleDataUpdates(value)) field = value
}
// Data for restoring initial value
override var defaultData: D? = null
set(value) {
field = value
data = value
}
// Use it for handling data updates in View
override var onDataUpdated: ValueChange<D>? = null
// When data updates directly it invokes onDataUpdated
// return true = data will update
// return false = data won't update
protected open fun handleDataUpdates(value: D?): Boolean {
ILog.d(this, "handleDateUpdates: $value")
onDataUpdated?.invoke(value)
return true
}
// Use it to update the data from a View. It disables onDataUpdated to prevent a callback loop
override fun updateDataByView(data: D?) {
ILog.d(this, "data changed: $data")
val callback = onDataUpdated
onDataUpdated = null
this.data = data
onDataUpdated = callback
}
}
class ViewState {
var isHiddenField = false
var descriptionVisibility: Int = 0x00000008 var descriptionVisibility: Int = 0x00000008
set(value) { set(value) {
@ -71,3 +26,60 @@ class ViewState {
var onDescriptionVisibilityChanged: SafeValueChange<Int>? = null var onDescriptionVisibilityChanged: SafeValueChange<Int>? = null
} }
interface ItemViewModel : PayloadHolder {
val viewState: ViewState
var data: Any?
var defaultData: Any?
var onDataUpdated: ValueChange<Any?>?
fun updateDataByView(data: Any?)
}
open class BaseItemViewModel(
value: Any? = null,
override val viewState: ViewState = ViewState()
) : ItemViewModel {
override val payload: Payload = mutableMapOf()
// Don't update it directly from a View. Use for it updateDataByView()
override var data: Any? = value
set(value) {
if (handleDataUpdates(value)) field = value
}
// Data for restoring initial value
override var defaultData: Any? = value
set(value) {
field = value
data = value
}
// Use it for handling data updates in View
override var onDataUpdated: ValueChange<Any?>? = null
// When data updates directly it invokes onDataUpdated
// return true = data will update
// return false = data won't update
protected open fun handleDataUpdates(value: Any?): Boolean {
ILog.d(this, "handleDateUpdates: $value")
onDataUpdated?.invoke(value)
return true
}
// Use it to update the data from a View. It disables onDataUpdated to prevent a callback loop
override fun updateDataByView(data: Any?) {
ILog.d(this, "data changed: $data")
val callback = onDataUpdated
onDataUpdated = null
this.data = data
onDataUpdated = callback
}
}
class ListViewModel(
val itemList: List<KeyValue>,
var selectedItem: Any?,
override val viewState: ViewState = ViewState()
) : BaseItemViewModel(selectedItem)

View file

@ -1,19 +1,39 @@
package com.tangem.tangemtest._arch.structure.impl package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.*
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class AnyItem(override val id: Id, value: Any? = null) : BaseItem<Any>(AnyViewModel(value))
class TextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
class EditTextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
class NumberItem(override val id: Id, value: Number? = null) : BaseItem<Number>(NumberViewModel(value))
class BoolItem(override val id: Id, value: Boolean? = null) : BaseItem<Boolean>(BoolViewModel(value))
class ListItem(override val id: Id, value: List<KeyValue>, selectedValue: Any?) open class TypedItem<D>(id: Id, viewModel: ItemViewModel) : BaseItem(id, viewModel) {
: BaseItem<ListValueWrapper>(ListViewModel(ListValueWrapper(selectedValue, value)) open fun getTypedData(): D? = viewModel.data as? D
) }
inline fun<reified T> BaseItem<T>.getData(): T? = viewModel.data class TextItem(id: Id, viewModel: ItemViewModel) : TypedItem<String>(id, viewModel) {
constructor(id: Id, value: String? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class NumberItem(id: Id, viewModel: ItemViewModel) : TypedItem<Number>(id, viewModel) {
constructor(id: Id, value: Number? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class BoolItem(id: Id, viewModel: ItemViewModel) : TypedItem<Boolean>(id, viewModel) {
constructor(id: Id, value: Boolean? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class EditTextItem(id: Id, viewModel: ItemViewModel) : TypedItem<String>(id, viewModel) {
constructor(id: Id, value: String? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class SpinnerItem(id: Id, viewModel: ListViewModel) : TypedItem<ListViewModel>(id, viewModel) {
constructor(id: Id, list: List<KeyValue>, selectedValue: Any?, viewState: ViewState = ViewState())
: this(id, ListViewModel(list, selectedValue, viewState))
}

View file

@ -1,29 +0,0 @@
package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel
/**
[REDACTED_AUTHOR]
*/
open class TransitiveViewModel<D>(value: D?) : BaseItemViewModel<D>() {
init {
defaultData = value
}
}
class AnyViewModel(value: Any? = null) : TransitiveViewModel<Any>(value)
class StringViewModel(value: String? = null) : TransitiveViewModel<String>(value)
class NumberViewModel(value: Number? = null) : TransitiveViewModel<Number>(value)
class BoolViewModel(value: Boolean? = null) : TransitiveViewModel<Boolean>(value)
class ListViewModel(value: ListValueWrapper? = null) : TransitiveViewModel<ListValueWrapper>(value) {
override fun handleDataUpdates(value: ListValueWrapper?): Boolean {
val newValue = value ?: return false
if (newValue.selectedItem == data?.selectedItem) return false
return super.handleDataUpdates(value)
}
}
class KeyValue(val key: String, val value: Any)
class ListValueWrapper(var selectedItem: Any?, val itemList: List<KeyValue>)

View file

@ -0,0 +1,12 @@
package com.tangem.tangemtest._arch.widget
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
/**
[REDACTED_AUTHOR]
*/
interface ItemWidgetBuilder {
fun build(item: BaseItem, parent: ViewGroup): ViewWidget?
}

View file

@ -0,0 +1,36 @@
package com.tangem.tangemtest._arch.widget
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
import com.tangem.tangemtest._arch.widget.impl.LinearGroupWidget
import com.tangem.tangemtest._arch.widget.impl.StubWidget
/**
[REDACTED_AUTHOR]
*/
class WidgetBuilder(
private val itemBuilder: ItemWidgetBuilder
) {
fun build(item: Item, parent: ViewGroup): ViewWidget? {
return when (item) {
is ItemGroup -> buildBlock(item, parent)
is BaseItem -> itemBuilder.build(item, parent)
else -> StubWidget(item.id, parent)
}
}
private fun buildBlock(itemGroup: ItemGroup, parent: ViewGroup): ViewWidget {
return when (itemGroup) {
is ItemGroup -> {
val linearBlock = LinearGroupWidget(parent, itemGroup)
itemGroup.getItems().forEach { build(it, linearBlock.view as ViewGroup) }
linearBlock
}
else -> StubWidget(itemGroup.id, parent)
}
}
}

View file

@ -0,0 +1,67 @@
package com.tangem.tangemtest._arch.widget.abstraction
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorRes
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 ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
/**
[REDACTED_AUTHOR]
*/
interface ViewWidget : LayoutHolder {
val view: View
var item: Item
fun getName(): String
fun setBackgroundColor(@ColorRes colorId: Int?)
}
abstract class BaseViewWidget(
parent: ViewGroup,
override var item: Item
) : ViewWidget {
override val view: View = inflate(getLayoutId(), parent)
private var defaultBackground: Drawable? = view.background
init {
if (item.viewModel.viewState.isHidden) {
view.visibility = View.GONE
} else {
setBackgroundColor(item.viewModel.viewState.backgroundColor)
}
}
override fun getName(): String {
return when (val id = item.id) {
is StringId -> id.value
is StringResId -> view.resources.getString(id.value)
else -> view.resources.getString(R.string.unknown)
}
}
override fun setBackgroundColor(colorId: Int?) {
val background = when {
colorId == null -> null
colorId == -1 -> defaultBackground
else -> ColorDrawable(view.colorFrom(colorId))
}
view.background = background
}
}
internal fun inflate(id: Int, parent: ViewGroup): View {
val layoutId = if (id <= 0) R.layout.w_empty else id
val inflatedView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
parent.addView(inflatedView)
return inflatedView
}

View file

@ -0,0 +1,18 @@
package com.tangem.tangemtest._arch.widget.impl
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup
import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget
/**
[REDACTED_AUTHOR]
*/
class LinearGroupWidget(
parent: ViewGroup,
itemGroup: ItemGroup
) : BaseViewWidget(parent, itemGroup) {
override fun getLayoutId(): Int = R.layout.w_personilize_block
}

View file

@ -0,0 +1,15 @@
package com.tangem.tangemtest._arch.widget.impl
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel
import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget
/**
[REDACTED_AUTHOR]
*/
class StubWidget(id: Id, parent: ViewGroup) : BaseViewWidget(parent, BaseItem(id, BaseItemViewModel())) {
override fun getLayoutId(): Int = R.layout.w_empty
}

View file

@ -2,7 +2,6 @@ package com.tangem.tangemtest._main
import android.content.res.Resources import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Menu import android.view.Menu
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -12,8 +11,9 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -34,8 +34,8 @@ class MainActivity : AppCompatActivity() {
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController val navController = host.navController
appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_entry_point)) appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration) toolbar.setupWithNavController(navController, appBarConfiguration)
navController.addOnDestinationChangedListener { _, destination, _ -> navController.addOnDestinationChangedListener { _, destination, _ ->
val dest: String = try { val dest: String = try {
@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() {
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
destination.id.toString() destination.id.toString()
} }
Log.d("NavigationActivity", "Navigated to $dest") Log.d(this, "Navigated to $dest")
} }
} }

View file

@ -2,6 +2,8 @@ package com.tangem.tangemtest._main
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.tangem.tasks.TaskEvent
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -9,7 +11,16 @@ import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() { class MainViewModel : ViewModel() {
val ldDescriptionSwitch = MutableLiveData<Boolean>(false) val ldDescriptionSwitch = MutableLiveData<Boolean>(false)
var responseEvent: TaskEvent<*>? = null
fun switchToggled(state: Boolean) { fun switchToggled(state: Boolean) {
ldDescriptionSwitch.postValue(state) ldDescriptionSwitch.postValue(state)
} }
fun changeResponseEvent(event: TaskEvent<*>?) {
Log.d(this, "changeResponseEvent")
val reqEvent = event ?: return
responseEvent = reqEvent
}
} }

View file

@ -1,14 +1,10 @@
package com.tangem.tangemtest._main.entryPoint package com.tangem.tangemtest._main.entryPoint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -19,21 +15,19 @@ import com.tangem.tangemtest._main.MainViewModel
import com.tangem.tangemtest.ucase.getDefaultNavigationOptions import com.tangem.tangemtest.ucase.getDefaultNavigationOptions
import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.resources.MainResourceHolder import com.tangem.tangemtest.ucase.resources.MainResourceHolder
import com.tangem.tangemtest.ucase.ui.BaseFragment
import kotlinx.android.synthetic.main.fg_entry_point.* import kotlinx.android.synthetic.main.fg_entry_point.*
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class ActionListFragment : Fragment() { class ActionListFragment : BaseFragment() {
private val navController: NavController by lazy { findNavController() }
private lateinit var rvActions: RecyclerView private lateinit var rvActions: RecyclerView
private val mainActivityVM: MainViewModel by activityViewModels() private val mainActivityVM: MainViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun getLayoutId(): Int = R.layout.fg_entry_point
return inflater.inflate(R.layout.fg_entry_point, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -48,7 +42,9 @@ class ActionListFragment : Fragment() {
val vhDataWrapper = VhExDataWrapper(MainResourceHolder, false) val vhDataWrapper = VhExDataWrapper(MainResourceHolder, false)
rvActions.adapter = RvActionsAdapter(vhDataWrapper) { type, position, data -> navigate(data) } rvActions.adapter = RvActionsAdapter(vhDataWrapper) { type, position, data ->
navigateTo(data, options = getDefaultNavigationOptions())
}
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
vhDataWrapper.descriptionIsVisible = it vhDataWrapper.descriptionIsVisible = it
TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition()) TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition())
@ -64,10 +60,6 @@ class ActionListFragment : Fragment() {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
private fun navigate(destinationId: Int) {
navController.navigate(destinationId, null, getDefaultNavigationOptions())
}
private fun getNavigateOptions(): MutableList<ActionType> { private fun getNavigateOptions(): MutableList<ActionType> {
return mutableListOf( return mutableListOf(
ActionType.Scan, ActionType.Scan,

View file

@ -0,0 +1,9 @@
package com.tangem.tangemtest.commons
/**
[REDACTED_AUTHOR]
*/
interface Store<M> {
fun save(config: M)
fun restore(): M
}

View file

@ -3,6 +3,7 @@ package com.tangem.tangemtest.commons
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
fun <A, B> performAction(a: A?, b: B?, action: (A, B) -> Unit) { fun <A, B> performAction(a: A?, b: B?, action: (A, B) -> Unit, onFail: (() -> Unit)? = null) {
if (a != null && b != null) action(a, b) if (a != null && b != null) action(a, b)
else onFail?.invoke()
} }

View file

@ -0,0 +1,22 @@
package com.tangem.tangemtest.extensions
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
/**
[REDACTED_AUTHOR]
*/
fun Fragment.shareText(text: String) {
requireActivity().shareText(text)
}
fun ComponentActivity.shareText(text: String) {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(shareIntent)
}

View file

@ -43,4 +43,6 @@ abstract class BaseAction : Action {
} }
callback(commandResult, modifiedItems) callback(commandResult, modifiedItems)
} }
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
} }

View file

@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.variants.TlvId import com.tangem.tangemtest.ucase.variants.TlvId
@ -11,7 +11,7 @@ import com.tangem.tangemtest.ucase.variants.TlvId
*/ */
class DepersonalizeAction : BaseAction() { class DepersonalizeAction : BaseAction() {
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
val item = attrs.itemList.findDataItem(TlvId.CardId) ?: return val item = attrs.itemList.findItem(TlvId.CardId) ?: return
val cardId = item.viewModel.data as? String ?: return val cardId = item.viewModel.data as? String ?: return
attrs.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) } attrs.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) }

View file

@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest._arch.structure.impl.EditTextItem import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest._arch.structure.impl.NumberItem import com.tangem.tangemtest._arch.structure.impl.NumberItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
@ -10,10 +10,10 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.ItemError import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToCardConfig import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig
import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizeParams import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizationParams
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/** /**
@ -33,12 +33,12 @@ class PersonalizeAction : BaseAction() {
return return
} }
val issuer = DefaultPersonalizeParams.issuer() val issuer = DefaultPersonalizationParams.issuer()
val acquirer = DefaultPersonalizeParams.acquirer() val acquirer = DefaultPersonalizationParams.acquirer()
val manufacturer = DefaultPersonalizeParams.manufacturer() val manufacturer = DefaultPersonalizationParams.manufacturer()
val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig()) val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig())
val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig) val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig)
attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) { attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) {
handleResult(payload, it, null, attrs, callback) handleResult(payload, it, null, attrs, callback)
@ -46,18 +46,18 @@ class PersonalizeAction : BaseAction() {
} }
private fun checkSeries(itemList: List<Item>): Boolean { private fun checkSeries(itemList: List<Item>): Boolean {
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false
val data = seriesItem.getData() ?: return false val data = seriesItem.getData() as? String ?: return false
seriesItem.setData(data.toUpperCase()) seriesItem.setData(data.toUpperCase())
return data.length == 2 || data.length == 4 return data.length == 2 || data.length == 4
} }
private fun checkNumber(itemList: List<Item>): Boolean { private fun checkNumber(itemList: List<Item>): Boolean {
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false
val numberItem = itemList.findDataItem(CardNumber.Number) as? NumberItem ?: return false val numberItem = itemList.findItem(CardNumber.Number) as? NumberItem ?: return false
val seriesData = seriesItem.getData() ?: return false val seriesData = seriesItem.getData() as? String ?: return false
val numberData = stringOf(numberItem.getData()) val numberData = stringOf(numberItem.getData() as? Number)
return if (seriesData.length == 2 && numberData.length > 13) false return if (seriesData.length == 2 && numberData.length > 13) false
else !(seriesData.length == 4 && numberData.length > 11) else !(seriesData.length == 4 && numberData.length > 11)

View file

@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.variants.TlvId import com.tangem.tangemtest.ucase.variants.TlvId
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
@ -12,9 +12,9 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
*/ */
class SignAction : BaseAction() { class SignAction : BaseAction() {
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
val dataForHashing = attrs.itemList.findDataItem(TlvId.TransactionOutHash) ?: return val dataForHashing = attrs.itemList.findItem(TlvId.TransactionOutHash) ?: return
val hash = dataForHashing.getData() as? ByteArray ?: return val hash = dataForHashing.getData() as? ByteArray ?: return
val cardId = attrs.itemList.findDataItem(TlvId.CardId)?.viewModel?.data ?: return val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return
attrs.tangemSdk.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } attrs.tangemSdk.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) }
} }

View file

@ -1,25 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager
import com.tangem.tangemtest.ucase.resources.ActionType
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class ParamsManagerFactory : BaseTypedHolder<ActionType, ItemsManager>() {
companion object {
fun createFactory(): ParamsManagerFactory {
return ParamsManagerFactory().apply {
register(ActionType.Scan, ScanItemsManager())
register(ActionType.Sign, SignItemsManager())
register(ActionType.Personalize, PersonalizeItemsManager())
register(ActionType.Depersonalize, DepersonalizeItemsManager())
}
}
}
}

View file

@ -19,12 +19,12 @@ typealias ActionCallback = (ActionResponse, AffectedList) -> Unit
interface ItemsManager : PayloadHolder { interface ItemsManager : PayloadHolder {
fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null) fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null)
fun setItems(items: List<Item>)
fun getItems(): List<Item> fun getItems(): List<Item>
fun setItemChangeConsequences(consequence: ItemsChangeConsequence?)
fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback)
fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)?
fun attachPayload(payload: Payload) fun attachPayload(payload: Payload)
val consequence: ItemsChangeConsequence?
} }
interface PayloadKey { interface PayloadKey {

View file

@ -1,39 +1,59 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import androidx.lifecycle.LifecycleObserver
import com.tangem.TangemSdk import com.tangem.TangemSdk
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest.ucase.domain.actions.Action import com.tangem.tangemtest.ucase.domain.actions.Action
import com.tangem.tangemtest.ucase.domain.actions.AttrForAction import com.tangem.tangemtest.ucase.domain.actions.AttrForAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.AffectedItemsCallback import com.tangem.tangemtest.ucase.domain.paramsManager.AffectedItemsCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
abstract class BaseItemsManager(protected val action: Action) : ItemsManager { open class BaseItemsManager(protected val action: Action) : ItemsManager, LifecycleObserver {
protected val itemList: List<Item> by lazy { createItemsList() } init {
Log.d(this, "new instance created")
override val consequence: ItemsChangeConsequence? = null }
override val payload: MutableMap<String, Any?> = mutableMapOf() override val payload: MutableMap<String, Any?> = mutableMapOf()
protected var changeConsequence: ItemsChangeConsequence? = null
protected val itemList: MutableList<Item> = mutableListOf()
override fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback?) { override fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback?) {
if (itemList.isEmpty()) return if (itemList.isEmpty()) return
val foundItem = itemList.findDataItem(id) ?: return val foundItem = itemList.findItem(id) ?: return
foundItem.setData(value) foundItem.setData(value)
applyChangesByAffectedItems(foundItem, callback) applyChangesByAffectedItems(foundItem, callback)
} }
override fun getItems(): MutableList<Item> = itemList.toMutableList() override fun setItems(items: List<Item>) {
itemList.clear()
itemList.addAll(items)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? = null override fun getItems(): List<Item> = itemList
override fun setItemChangeConsequences(consequence: ItemsChangeConsequence?) {
this.changeConsequence = consequence
}
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
override fun attachPayload(payload: Payload) { override fun attachPayload(payload: Payload) {
payload.forEach { this.payload[it.key] = it.value } payload.forEach { this.payload[it.key] = it.value }
@ -41,11 +61,9 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager {
// Use it if current item needs to be affect any other items // Use it if current item needs to be affect any other items
protected open fun applyChangesByAffectedItems(param: Item, callback: AffectedItemsCallback?) { protected open fun applyChangesByAffectedItems(param: Item, callback: AffectedItemsCallback?) {
consequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) } changeConsequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) }
} }
protected fun getAttrsForAction(tangemSdk: TangemSdk) protected fun getAttrsForAction(tangemSdk: TangemSdk)
: AttrForAction = AttrForAction(tangemSdk, itemList, payload, consequence) : AttrForAction = AttrForAction(tangemSdk, itemList, payload, changeConsequence)
abstract fun createItemsList(): List<Item>
} }

View file

@ -1,26 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.TangemSdk
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.domain.actions.DepersonalizeAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.variants.TlvId
/**
[REDACTED_AUTHOR]
*/
class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) {
override fun createItemsList(): List<Item> {
return listOf(EditTextItem(TlvId.CardId, null))
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
}

View file

@ -0,0 +1,35 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.OnLifecycleEvent
import com.tangem.CardManager
import com.tangem.tangemtest.commons.Store
import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizationItemsManager(
private val store: Store<PersonalizationConfig>
) : BaseItemsManager(PersonalizeAction()) {
private val converter = PersonalizationConfigConverter()
init {
val config = store.restore()
setItems(converter.convert(config))
}
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
val config = converter.convert(itemList, PersonalizationConfig())
store.save(config)
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.TangemSdk
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
/**
[REDACTED_AUTHOR]
*/
class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) {
override fun createItemsList(): List<Item> = listOf()
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.TangemSdk
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest.ucase.domain.actions.ScanAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
/**
[REDACTED_AUTHOR]
*/
class ScanItemsManager : BaseItemsManager(ScanAction()) {
override fun createItemsList(): List<Item> = listOf()
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
}

View file

@ -1,34 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.TangemSdk
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.domain.actions.SignAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.SignScanConsequence
import com.tangem.tangemtest.ucase.variants.TlvId
/**
[REDACTED_AUTHOR]
*/
class SignItemsManager : BaseItemsManager(SignAction()) {
override val consequence: ItemsChangeConsequence? by lazy { SignScanConsequence() }
override fun createItemsList(): List<Item> {
return listOf(
EditTextItem(TlvId.CardId, null),
EditTextItem(TlvId.TransactionOutHash, "Data used for hashing")
)
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.domain.actions.DepersonalizeAction
import com.tangem.tangemtest.ucase.domain.actions.ScanAction
import com.tangem.tangemtest.ucase.domain.actions.SignAction
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.SignScanConsequence
import com.tangem.tangemtest.ucase.variants.TlvId
/**
[REDACTED_AUTHOR]
*/
class ScanItemsManager : BaseItemsManager(ScanAction())
class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) {
init {
setItems(listOf(EditTextItem(TlvId.CardId, null)))
}
}
class SignItemsManager : BaseItemsManager(SignAction()) {
init {
setItemChangeConsequences(SignScanConsequence())
setItems(listOf(
EditTextItem(TlvId.CardId, null),
EditTextItem(TlvId.TransactionOutHash, "Data used for hashing")
))
}
}

View file

@ -5,7 +5,7 @@ import com.tangem.commands.CardStatus
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError import com.tangem.tangemtest.ucase.tunnel.CardError
@ -18,18 +18,18 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI
*/ */
class AfterScanModifier : AfterActionModification { class AfterScanModifier : AfterActionModification {
override fun modify(payload: PayloadHolder, commandResult: CompletionResult<*>, itemList: List<Item>): List<Item> { override fun modify(payload: PayloadHolder, commandResult: CompletionResult<*>, itemList: List<Item>): List<Item> {
val foundItem = itemList.findDataItem(TlvId.CardId) ?: return listOf() val foundItem = itemList.findItem(TlvId.CardId) ?: return listOf()
val card = smartCast(commandResult) ?: return listOf() val card = smartCast(commandResult) ?: return listOf()
val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf() val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf()
return if (isNotPersonalized(card)) { return if (isNotPersonalized(card)) {
actionView.showSnackbar(CardError.NotPersonalized) actionView.showSnackbar(CardError.NotPersonalized)
postUI { actionView.showActionFab(false) } postUI { actionView.enableActionFab(false) }
listOf() listOf()
} else { } else {
payload.set(PayloadKey.card, card) payload.set(PayloadKey.card, card)
foundItem.setData(card.cardId) foundItem.setData(card.cardId)
postUI { actionView.showActionFab(true) } postUI { actionView.enableActionFab(true) }
listOf(foundItem) listOf(foundItem)
} }
} }

View file

@ -6,7 +6,7 @@ import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.calculateSha512 import com.tangem.common.extensions.calculateSha512
import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.variants.TlvId import com.tangem.tangemtest.ucase.variants.TlvId
@ -27,12 +27,12 @@ class SignScanConsequence: ItemsChangeConsequence {
override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>? { override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>? {
if (changedItem.id != TlvId.CardId) return null if (changedItem.id != TlvId.CardId) return null
val hashItem = itemList.findDataItem(TlvId.TransactionOutHash) ?: return null val hashItem = itemList.findItem(TlvId.TransactionOutHash) ?: return null
val affectedItems = mutableListOf(hashItem) val affectedItems = mutableListOf(hashItem)
val card = payload.remove(PayloadKey.card) as Card? val card = payload.remove(PayloadKey.card) as Card?
if (card == null) { if (card == null) {
hashItem.restoreDefaultData() hashItem.restoreDefaultData()
postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.showActionFab(false) } postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.enableActionFab(false) }
} else { } else {
val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s" val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s"
val hashedData = when (card.curve) { val hashedData = when (card.curve) {

View file

@ -6,6 +6,7 @@ import com.tangem.commands.Settings
import com.tangem.commands.SettingsMask import com.tangem.commands.SettingsMask
import com.tangem.commands.SigningMethod import com.tangem.commands.SigningMethod
import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toHexString
import com.tangem.tasks.TaskEvent
import java.lang.reflect.Type import java.lang.reflect.Type
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
@ -13,7 +14,7 @@ import java.util.*
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class GsonInitializer { class ResponseJsonConverter {
val gson: Gson by lazy { init() } val gson: Gson by lazy { init() }
private fun init(): Gson { private fun init(): Gson {
@ -27,6 +28,13 @@ class GsonInitializer {
builder.setPrettyPrinting() builder.setPrettyPrinting()
return builder.create() return builder.create()
} }
fun convertTaskEvent(task: TaskEvent<*>?): String = gson.toJson(task)
fun convertEvent(task: TaskEvent<*>?): String {
val event = task as? TaskEvent.Event ?: ""
return gson.toJson(event)
}
} }
class ByteTypeAdapter : JsonSerializer<ByteArray> { class ByteTypeAdapter : JsonSerializer<ByteArray> {

View file

@ -1,9 +1,13 @@
package com.tangem.tangemtest.ucase.resources package com.tangem.tangemtest.ucase.resources
import com.tangem.tangemtest.AppTangemDemo
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.StringId
import com.tangem.tangemtest._arch.structure.StringResId
import com.tangem.tangemtest.ucase.resources.initializers.ActionResources import com.tangem.tangemtest.ucase.resources.initializers.ActionResources
import com.tangem.tangemtest.ucase.resources.initializers.PersonalizeResources import com.tangem.tangemtest.ucase.resources.initializers.PersonalizationResources
import com.tangem.tangemtest.ucase.resources.initializers.ResponseResources
import com.tangem.tangemtest.ucase.resources.initializers.TlvResources import com.tangem.tangemtest.ucase.resources.initializers.TlvResources
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
@ -14,9 +18,27 @@ open class ResourceHolder<T> : BaseTypedHolder<T, Resources>()
object MainResourceHolder : ResourceHolder<Id>() { object MainResourceHolder : ResourceHolder<Id>() {
init { init {
PersonalizeResources().init(this) PersonalizationResources().init(this)
ActionResources().init(this) ActionResources().init(this)
TlvResources().init(this) TlvResources().init(this)
ResponseResources().init(this)
}
override fun get(type: Id): Resources? {
return when(type) {
is StringId -> getResourcesFromName(type.value)
is StringResId -> Resources(type.value)
else -> super.get(type)
}
}
private fun getResourcesFromName(resName: String): Resources? {
val context = AppTangemDemo.appInstance.applicationContext
val resNameId = context.resources.getIdentifier(resName, "string", context.packageName)
val resInfoId = context.resources.getIdentifier("info_$resName", "string", context.packageName)
if (resNameId == 0) return null
return Resources(resNameId, if (resInfoId == 0) null else resInfoId)
} }
inline fun <reified Res : Resources> safeGet(id: Id): Res { inline fun <reified Res : Resources> safeGet(id: Id): Res {

View file

@ -10,7 +10,7 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.TypedHolder
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class PersonalizeResources() { class PersonalizationResources() {
fun init(holder: ResourceHolder<Id>) { fun init(holder: ResourceHolder<Id>) {
initBlock(holder) initBlock(holder)
initCardNumber(holder) initCardNumber(holder)

View file

@ -0,0 +1,53 @@
package com.tangem.tangemtest.ucase.resources.initializers
import com.tangem.tangemtest.R
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
import com.tangem.tangemtest.ucase.resources.Resources
import com.tangem.tangemtest.ucase.variants.responses.CardDataId
import com.tangem.tangemtest.ucase.variants.responses.CardId
/**
[REDACTED_AUTHOR]
*/
class ResponseResources {
fun init(holder: MainResourceHolder) {
initCard(holder)
initCardData(holder)
}
private fun initCard(holder: MainResourceHolder) {
holder.register(CardId.cardId, Resources(R.string.response_card_cid, R.string.info_response_card_cid))
holder.register(CardId.manufacturerName, Resources(R.string.response_card_manufacturer_name, R.string.info_response_card_manufacturer_name))
holder.register(CardId.status, Resources(R.string.response_card_status, R.string.info_response_card_status))
holder.register(CardId.firmwareVersion, Resources(R.string.response_card_firmware_version, R.string.info_response_card_firmware_version))
holder.register(CardId.cardPublicKey, Resources(R.string.response_card_public_key, R.string.info_response_card_public_key))
holder.register(CardId.settingsMask, Resources(R.string.response_card_settings_mask, R.string.info_response_card_settings_mask))
holder.register(CardId.cardData, Resources(R.string.response_card_card_data, R.string.info_response_card_card_data))
holder.register(CardId.issuerPublicKey, Resources(R.string.response_card_issuer_data_public_key, R.string.info_response_card_issuer_data_public_key))
holder.register(CardId.curve, Resources(R.string.response_card_curve, R.string.info_response_card_curve))
holder.register(CardId.maxSignatures, Resources(R.string.response_card_max_signatures, R.string.info_response_card_max_signatures))
holder.register(CardId.signingMethod, Resources(R.string.response_card_signing_method, R.string.response_card_signing_method))
holder.register(CardId.pauseBeforePin2, Resources(R.string.response_card_pause_before_pin2, R.string.info_response_card_allow_pin2))
holder.register(CardId.walletPublicKey, Resources(R.string.response_card_wallet_public_key, R.string.info_response_card_wallet_public_key))
holder.register(CardId.walletRemainingSignatures, Resources(R.string.response_card_wallet_remaining_signatures, R.string.info_response_card_wallet_remaining_signatures))
holder.register(CardId.walletSignedHashes, Resources(R.string.response_card_wallet_signed_hashes, R.string.info_response_card_wallet_signed_hashes))
holder.register(CardId.health, Resources(R.string.response_card_health, R.string.info_response_card_health))
holder.register(CardId.isActivated, Resources(R.string.response_card_is_activated, R.string.info_response_card_is_activated))
holder.register(CardId.activationSeed, Resources(R.string.response_card_activation_seed, R.string.info_response_card_activation_seed))
holder.register(CardId.paymentFlowVersion, Resources(R.string.response_card_payment_flow_version, R.string.info_response_card_payment_flow_version))
holder.register(CardId.userCounter, Resources(R.string.response_card_user_counter, R.string.info_response_card_user_counter))
holder.register(CardId.userProtectedCounter, Resources(R.string.response_card_user_protected_counter, R.string.info_response_card_user_protected_counter))
}
private fun initCardData(holder: MainResourceHolder) {
holder.register(CardDataId.batchId, Resources(R.string.response_card_card_data_batch_id, R.string.info_response_card_card_data_batch_id))
holder.register(CardDataId.manufactureDateTime, Resources(R.string.response_card_card_data_manufacture_date_time, R.string.info_response_card_card_data_manufacture_date_time))
holder.register(CardDataId.issuerName, Resources(R.string.response_card_card_data_issuer_name, R.string.info_response_card_card_data_issuer_name))
holder.register(CardDataId.blockchainName, Resources(R.string.response_card_card_data_blockchain_name, R.string.info_response_card_card_data_blockchain_name))
holder.register(CardDataId.manufacturerSignature, Resources(R.string.response_card_card_data_manufacturer_signature, R.string.info_response_card_card_data_manufacturer_signature))
holder.register(CardDataId.productMask, Resources(R.string.response_card_card_data_product_mask, R.string.info_response_card_card_data_product_mask))
holder.register(CardDataId.tokenSymbol, Resources(R.string.response_card_card_data_token_symbol, R.string.info_response_card_card_data_token_symbol))
holder.register(CardDataId.tokenContractAddress, Resources(R.string.response_card_card_data_token_contract_address, R.string.info_response_card_card_data_token_contract_address))
holder.register(CardDataId.tokenDecimal, Resources(R.string.response_card_card_data_token_decimal, R.string.info_response_card_card_data_token_decimal))
}
}

View file

@ -1,15 +1,16 @@
package com.tangem.tangemtest.ucase.tunnel package com.tangem.tangemtest.ucase.tunnel
import com.google.android.material.snackbar.Snackbar
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
interface SnackbarHolder {
fun showSnackbar(message: String, length: Int = Snackbar.LENGTH_SHORT)
fun showSnackbar(id: Int, length: Int = Snackbar.LENGTH_SHORT)
}
interface ViewScreen interface ViewScreen
interface SnackbarHolder {
fun showSnackbar(message: String)
fun showSnackbar(id: Int)
fun showSnackbar(id: Id)
}
interface ActionView : ViewScreen, SnackbarHolder { interface ActionView : ViewScreen, SnackbarHolder {
fun showActionFab(show: Boolean) fun enableActionFab(enable: Boolean)
fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)? = null)
} }

View file

@ -1,5 +1,6 @@
package com.tangem.tangemtest.ucase.ui package com.tangem.tangemtest.ucase.ui
import android.view.View
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.lifecycle.* import androidx.lifecycle.*
import com.google.gson.Gson import com.google.gson.Gson
@ -12,9 +13,10 @@ import com.tangem.tangemtest._arch.SingleLiveEvent
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.iterate
import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.commons.performAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.responses.GsonInitializer import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter
import com.tangem.tangemtest.ucase.tunnel.ViewScreen import com.tangem.tangemtest.ucase.tunnel.ViewScreen
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
@ -22,17 +24,16 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() { class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = ParamsViewModel(manager) as T override fun <T : ViewModel?> create(modelClass: Class<T>): T = ActionViewModel(manager) as T
} }
class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver {
val ldCard = MutableLiveData<Card>() val seResponseEvent = SingleLiveEvent<TaskEvent<*>>()
val ldIsVerified = MutableLiveData<Boolean>() val seReadResponse = SingleLiveEvent<String>()
val ldResponse = MutableLiveData<String>() val seResponse = SingleLiveEvent<String>()
val ldReadResponse = MutableLiveData<String>()
val ldParams = MutableLiveData(itemsManager.getItems())
val ldItemList = MutableLiveData(itemsManager.getItems())
val seError: MutableLiveData<String> = SingleLiveEvent() val seError: MutableLiveData<String> = SingleLiveEvent()
val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent() val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent()
@ -43,6 +44,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
this.tangemSdk = tangemSdk this.tangemSdk = tangemSdk
} }
@Deprecated("Events must be send directly from the Widget")
fun userChangedItem(id: Id, value: Any?) { fun userChangedItem(id: Id, value: Any?) {
itemChanged(id, value) itemChanged(id, value)
} }
@ -53,11 +55,11 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
//invokes Scan, Sign etc... //invokes Scan, Sign etc...
fun invokeMainAction() { fun invokeMainAction() {
performAction(itemsManager, tangemSdk) { paramsManager, cardManager -> performAction(itemsManager, tangemSdk) { paramsManager, tangemSdk ->
paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams -> paramsManager.invokeMainAction(tangemSdk) { response, listOfChangedParams ->
notifier.handleActionResult(response, listOfChangedParams) notifier.handleActionResult(response, listOfChangedParams)
} }
} })
} }
fun getItemAction(id: Id): (() -> Unit)? { fun getItemAction(id: Id): (() -> Unit)? {
@ -70,6 +72,12 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
} }
} }
fun toggleDescriptionVisibility(state: Boolean) {
ldItemList.value?.iterate {
it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
}
fun attachToPayload(payload: Payload) { fun attachToPayload(payload: Payload) {
itemsManager.attachPayload(payload) itemsManager.attachPayload(payload)
} }
@ -82,10 +90,10 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
} }
} }
internal class Notifier(private val vm: ParamsViewModel) { internal class Notifier(private val vm: ActionViewModel) {
private var notShowedError: SessionError? = null private var notShowedError: SessionError? = null
private val gson: Gson = GsonInitializer().gson private val gson: Gson = ResponseJsonConverter().gson
fun handleActionResult(response: CompletionResult<*>, list: List<Item>) { fun handleActionResult(response: CompletionResult<*>, list: List<Item>) {
if (list.isNotEmpty()) notifyItemsChanged(list) if (list.isNotEmpty()) notifyItemsChanged(list)
@ -102,7 +110,10 @@ internal class Notifier(private val vm: ParamsViewModel) {
when (commandResponse) { when (commandResponse) {
is CompletionResult.Failure -> handleError(commandResponse.error) is CompletionResult.Failure -> handleError(commandResponse.error)
is CompletionResult.Success -> handleDataEvent(commandResponse.data) is CompletionResult.Success -> {
handleDataEvent(commandResponse.data)
vm.seResponseEvent.postValue(commandResponse)
}
} }
} }
@ -110,9 +121,9 @@ internal class Notifier(private val vm: ParamsViewModel) {
when (data) { when (data) {
is Card -> { is Card -> {
vm.ldCard.postValue(data) vm.ldCard.postValue(data)
vm.ldReadResponse.postValue(gson.toJson(data)) vm.seReadResponse.postValue(gson.toJson(data))
} }
else -> vm.ldResponse.postValue(gson.toJson(data)) else -> vm.seResponse.postValue(gson.toJson(data))
} }
} }

View file

@ -4,9 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import androidx.core.view.plusAssign
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
@ -17,134 +15,127 @@ import com.tangem.TangemSdk
import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest._main.MainViewModel
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError import com.tangem.tangemtest.ucase.tunnel.CardError
import com.tangem.tangemtest.ucase.ui.widgets.ParameterWidget import com.tangem.tangemtest.ucase.ui.widgets.ParameterWidget
import ru.dev.gbixahue.eu4d.lib.android._android.views.enable
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { abstract class BaseCardActionFragment : BaseFragment(), ActionView {
protected val incomingParamsContainer: ViewGroup by lazy { protected lateinit var itemContainer: ViewGroup
mainView.findViewById<LinearLayout>(R.id.ll_incoming_params_container) protected lateinit var actionFab: FloatingActionButton
}
protected lateinit var mainView: View
protected val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) } protected abstract val itemsManager: ItemsManager
protected val widgetList = mutableListOf<ParameterWidget>() protected val mainActivityVM by activityViewModels<MainViewModel>()
protected val actionVM: ActionViewModel by viewModels { ActionViewModelFactory(itemsManager) }
protected val UNDEFINED = -1
private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! } private val paramsWidgetList = mutableListOf<ParameterWidget>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(this, "onCreateView")
mainView = inflater.inflate(getLayoutId(), container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Log.d(this, "onViewCreated") Log.d(this, "onViewCreated")
viewLifecycleOwner.lifecycle.addObserver(paramsVM) bindViews()
paramsVM.setCardManager(TangemSdk.init(requireActivity())) viewLifecycleOwner.lifecycle.addObserver(actionVM)
paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) actionVM.setCardManager(TangemSdk.init(requireActivity()))
actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
initFab() initFab()
showActionFab(false)
createWidgets { subscribeToViewModelChanges() } createWidgets { subscribeToViewModelChanges() }
} }
private fun initFab() { protected open fun bindViews() {
Log.d(this, "initFab") itemContainer = mainView.findViewById(R.id.ll_container)
actionFab.setOnClickListener { paramsVM.invokeMainAction() } actionFab = mainView.findViewById(R.id.fab_action)
} }
private fun createWidgets(widgetCreatedCallback: () -> Unit) { protected open fun initFab() {
enableActionFab(false)
actionFab.setOnClickListener { actionVM.invokeMainAction() }
}
protected open fun createWidgets(widgetCreatedCallback: () -> Unit) {
Log.d(this, "createWidgets") Log.d(this, "createWidgets")
paramsVM.ldParams.observe(viewLifecycleOwner, Observer { itemList -> actionVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList ->
itemList.forEach { param -> itemList.forEach { param ->
val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param) val widget = ParameterWidget(inflateParamView(itemContainer), param)
widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) } widget.onValueChanged = { id, value -> actionVM.userChangedItem(id, value) }
widget.onActionBtnClickListener = paramsVM.getItemAction(param.id) widget.onActionBtnClickListener = actionVM.getItemAction(param.id)
widgetList.add(widget) paramsWidgetList.add(widget)
} }
widgetCreatedCallback() widgetCreatedCallback()
}) })
} }
private fun subscribeToViewModelChanges() { protected open fun subscribeToViewModelChanges() {
Log.d(this, "subscribeToViewModelChanges") Log.d(this, "subscribeToViewModelChanges")
listenEvent()
listenReadResponse()
listenResponse() listenResponse()
listenError() listenError()
listenChangedItems() listenChangedItems()
listenDescriptionSwitchChanges() listenDescriptionSwitchChanges()
} }
protected open fun listenEvent() {
actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer {
mainActivityVM.changeResponseEvent(it)
})
}
protected open fun listenReadResponse() {}
protected open fun listenResponse() { protected open fun listenResponse() {
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) } actionVM.seResponse.observe(viewLifecycleOwner, Observer {
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer { navigateTo(R.id.action_nav_card_action_to_response_screen)
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
tvResponse.text = it
}) })
} }
protected open fun listenError() { protected open fun listenError() {
paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) actionVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) })
} }
@Deprecated("Start to use itemViewModel")
protected open fun listenChangedItems() { protected open fun listenChangedItems() {
paramsVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList -> actionVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList ->
itemList.forEach { item -> itemList.forEach { item ->
Log.d(this, "item changed from VM - name: ${item.id}") Log.d(this, "item changed from VM - name: ${item.id}, value:${item.viewModel.data}")
val dataItem = item as? BaseItem<Any?> ?: return@Observer paramsWidgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
Log.d(this, "item changed from VM - name: ${dataItem.id}, value:${dataItem.viewModel.data}")
widgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
} }
}) })
} }
protected open fun listenDescriptionSwitchChanges() { protected open fun listenDescriptionSwitchChanges() {
val mainActViewModel by activityViewModels<MainViewModel>() mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
mainActViewModel.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { isEnabled -> actionVM.toggleDescriptionVisibility(it)
widgetList.forEach { it.toggleDescriptionVisibility(isEnabled) }
}) })
} }
private fun inflateParamView(where: ViewGroup): ViewGroup { private fun inflateParamView(where: ViewGroup): ViewGroup {
val inflater = LayoutInflater.from(where.context) val inflater = LayoutInflater.from(where.context)
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false) val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)
where.addView(view) where.plusAssign(view)
return view as ViewGroup return view as ViewGroup
} }
override fun showActionFab(show: Boolean) { override fun enableActionFab(enable: Boolean) {
if (show) actionFab.show() else actionFab.hide() actionFab.enable(enable)
} }
override fun showSnackbar(id: Id) { override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) {
// MainResourceHolder.safeGet<>() val resourceId = when (id) {
when (id) { CardError.NotPersonalized -> R.string.card_error_not_personalized
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized) else -> additionalHandler?.invoke(id) ?: UNDEFINED
else -> showSnackbar(requireContext().getString(R.string.unknown))
}
} }
override fun showSnackbar(id: Int) { if (resourceId != UNDEFINED) showSnackbar(resourceId)
showSnackbar(requireContext().getString(id))
} }
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
abstract fun getAction(): ActionType
} }

View file

@ -0,0 +1,38 @@
package com.tangem.tangemtest.ucase.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.tangem.tangemtest.ucase.getDefaultNavigationOptions
import com.tangem.tangemtest.ucase.tunnel.SnackbarHolder
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
/**
[REDACTED_AUTHOR]
*/
abstract class BaseFragment : Fragment(), LayoutHolder, SnackbarHolder {
protected lateinit var mainView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mainView = inflater.inflate(getLayoutId(), container, false)
return mainView
}
protected fun navigateTo(navigationId: Int, bundle: Bundle? = null, options: NavOptions? = getDefaultNavigationOptions()) {
findNavController(this).navigate(navigationId, bundle, options)
}
override fun showSnackbar(id: Int, length: Int) {
showSnackbar(requireContext().getString(id), length)
}
override fun showSnackbar(message: String, length: Int) {
Snackbar.make(mainView, message, length).show()
}
}

View file

@ -12,8 +12,8 @@ import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.resources.MainResourceHolder import com.tangem.tangemtest.ucase.resources.MainResourceHolder
import com.tangem.tangemtest.ucase.resources.Resources import com.tangem.tangemtest.ucase.resources.Resources
@ -29,7 +29,7 @@ class ParameterWidget(
) { ) {
val id: Id = item.id val id: Id = item.id
val dataItem: BaseItem<Any> = item as BaseItem<Any> val typedItem = item as EditTextItem
var onValueChanged: ((Id, Any?) -> Unit)? = null var onValueChanged: ((Id, Any?) -> Unit)? = null
var onActionBtnClickListener: (() -> Unit)? = null var onActionBtnClickListener: (() -> Unit)? = null
@ -47,11 +47,11 @@ class ParameterWidget(
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) } private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
private var actionBtnVisibilityState: Int = btnAction.visibility private var actionBtnVisibilityState: Int = btnAction.visibility
private var value: Any? = dataItem.viewModel.data private var value: Any? = typedItem.viewModel.data
init { init {
tilValue.hint = tilValue.context.getString(getResNameId()) tilValue.hint = tilValue.context.getString(getResNameId())
etValue.setText(stringOf(dataItem.viewModel.data)) etValue.setText(stringOf(typedItem.viewModel.data))
etValue.addTextChangedListener(valueWatcher) etValue.addTextChangedListener(valueWatcher)
btnAction.setOnClickListener { onActionBtnClickListener?.invoke() } btnAction.setOnClickListener { onActionBtnClickListener?.invoke() }
btnAction.setText(getResNameId(ActionType.Scan)) btnAction.setText(getResNameId(ActionType.Scan))

View file

@ -1,7 +1,8 @@
package com.tangem.tangemtest.ucase.variants.depersonalize.ui package com.tangem.tangemtest.ucase.variants.depersonalize.ui
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
/** /**
@ -9,8 +10,7 @@ import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
*/ */
class DepersonalizeActionFragment : BaseCardActionFragment() { class DepersonalizeActionFragment : BaseCardActionFragment() {
override val itemsManager: ItemsManager by lazy { DepersonalizeItemsManager() }
override fun getLayoutId(): Int = R.layout.fg_depersonalize override fun getLayoutId(): Int = R.layout.fg_depersonalize
override fun getAction(): ActionType = ActionType.Depersonalize
} }

View file

@ -6,11 +6,9 @@ import com.tangem.tangemtest._arch.structure.Id
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface PersonalizeId : Id interface PersonalizationId : Id
interface BlockItem : PersonalizeId enum class BlockId : PersonalizationId {
enum class BlockId : PersonalizeId {
CardNumber, CardNumber,
Common, Common,
SigningMethod, SigningMethod,
@ -24,13 +22,13 @@ enum class BlockId : PersonalizeId {
Pins, Pins,
} }
enum class CardNumber : BlockItem { enum class CardNumber : PersonalizationId {
Series, Series,
Number, Number,
BatchId, BatchId,
} }
enum class Common : BlockItem { enum class Common : PersonalizationId {
Curve, Curve,
Blockchain, Blockchain,
BlockchainCustom, BlockchainCustom,
@ -38,7 +36,7 @@ enum class Common : BlockItem {
CreateWallet, CreateWallet,
} }
enum class SigningMethod : BlockItem { enum class SigningMethod : PersonalizationId {
SignTx, SignTx,
SignTxRaw, SignTxRaw,
SignValidatedTx, SignValidatedTx,
@ -48,7 +46,7 @@ enum class SigningMethod : BlockItem {
SignExternal, SignExternal,
} }
enum class SignHashExProp : BlockItem { enum class SignHashExProp : PersonalizationId {
PinLessFloorLimit, PinLessFloorLimit,
CryptoExKey, CryptoExKey,
RequireTerminalCertSig, RequireTerminalCertSig,
@ -56,26 +54,26 @@ enum class SignHashExProp : BlockItem {
CheckPin3, CheckPin3,
} }
enum class Denomination : BlockItem { enum class Denomination : PersonalizationId {
WriteOnPersonalize, WriteOnPersonalize,
Denomination, Denomination,
} }
enum class Token : BlockItem { enum class Token : PersonalizationId {
ItsToken, ItsToken,
Symbol, Symbol,
ContractAddress, ContractAddress,
Decimal Decimal
} }
enum class ProductMask : BlockItem { enum class ProductMask : PersonalizationId {
Note, Note,
Tag, Tag,
IdCard, IdCard,
IdIssuerCard IdIssuerCard
} }
enum class SettingsMask : BlockItem { enum class SettingsMask : PersonalizationId {
IsReusable, IsReusable,
NeedActivation, NeedActivation,
ForbidPurge, ForbidPurge,
@ -94,12 +92,12 @@ enum class SettingsMask : BlockItem {
RestrictOverwriteExtraIssuerData, RestrictOverwriteExtraIssuerData,
} }
enum class SettingsMaskProtocolEnc : BlockItem { enum class SettingsMaskProtocolEnc : PersonalizationId {
AllowUnencrypted, AllowUnencrypted,
AllowStaticEncryption AllowStaticEncryption
} }
enum class SettingsMaskNdef : BlockItem { enum class SettingsMaskNdef : PersonalizationId {
UseNdef, UseNdef,
DynamicNdef, DynamicNdef,
DisablePrecomputedNdef, DisablePrecomputedNdef,
@ -108,7 +106,7 @@ enum class SettingsMaskNdef : BlockItem {
Uri, Uri,
} }
enum class Pins : BlockItem { enum class Pins : PersonalizationId {
Pin, Pin,
Pin2, Pin2,
Pin3, Pin3,

View file

@ -0,0 +1,26 @@
package com.tangem.tangemtest.ucase.variants.personalize
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import com.google.gson.Gson
import com.tangem.tangemtest.AppTangemDemo
import com.tangem.tangemtest.commons.Store
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
class PersonalizationConfigStore(context: Context) : Store<PersonalizationConfig> {
private val key = "personalization_config"
private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences()
private val gson: Gson = Gson()
override fun save(config: PersonalizationConfig) {
sp.edit(true) { putString(key, gson.toJson(config)) }
}
override fun restore(): PersonalizationConfig {
val json = sp.getString(key, gson.toJson(PersonalizationConfig()))
return gson.fromJson(json, PersonalizationConfig::class.java)
}
}

View file

@ -2,18 +2,18 @@ package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.commands.EllipticCurve import com.tangem.commands.EllipticCurve
import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.impl.KeyValue import com.tangem.tangemtest._arch.structure.abstraction.KeyValue
import com.tangem.tangemtest.ucase.variants.personalize.* import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() { class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
// All registered association values can't be objects // All registered association values can't be objects
fun init(default: PersonalizeConfig) { fun init(default: PersonalizationConfig) {
register(CardNumber.Series, Value(default.series)) register(CardNumber.Series, Value(default.series))
register(CardNumber.Number, Value(default.startNumber)) register(CardNumber.Number, Value(default.startNumber))
register(CardNumber.BatchId, Value(default.batchId)) register(CardNumber.BatchId, Value(default.batchId))

View file

@ -1,4 +1,4 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.tangemtest.ucase.variants.personalize.* import com.tangem.tangemtest.ucase.variants.personalize.*

View file

@ -0,0 +1,303 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.tangemtest._arch.structure.Additional
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.*
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizationConfigConverter : ModelConverter<PersonalizationConfig> {
private val toModel: ItemsToModel<PersonalizationConfig> = ItemsToPersonalizationConfig()
private val toItems: ModelToItems<PersonalizationConfig> = PersonalizationConfigToItems()
override fun convert(from: List<Item>, default: PersonalizationConfig): PersonalizationConfig {
return toModel.convert(from, default)
}
override fun convert(from: PersonalizationConfig): List<Item> {
return toItems.convert(from)
}
}
class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
protected val valuesHolder = ConfigValuesHolder()
override fun convert(from: List<Item>, default: PersonalizationConfig): PersonalizationConfig {
valuesHolder.init(default)
mapListItems(from)
return createModel()
}
private fun mapListItems(itemList: List<Item>) {
itemList.iterate { item -> mapItemToHolder(item) }
}
private fun mapItemToHolder(item: Item) {
when (item) {
is ItemGroup -> mapListItems(item.itemList)
is BaseItem -> {
val defValue = valuesHolder.get(item.id) ?: return
defValue.set(item.viewModel.data)
}
}
}
private fun createModel(): PersonalizationConfig {
val export = PersonalizationConfig()
export.series = getTyped(CardNumber.Series)
export.startNumber = getTyped(CardNumber.Number)
export.curveID = getTyped(Common.Curve)
export.blockchain = getTyped(Common.Blockchain)
export.blockchainCustom = getTyped(Common.BlockchainCustom)
export.MaxSignatures = getTyped(Common.MaxSignatures)
export.createWallet = getTyped(Common.CreateWallet)
export.SigningMethod0 = getTyped(SigningMethod.SignTx)
export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw)
export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx)
export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw)
export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer)
export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer)
export.SigningMethod6 = getTyped(SigningMethod.SignExternal)
export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit)
export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey)
export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig)
export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig)
export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3)
export.writeOnPersonalization = getTyped(Denomination.WriteOnPersonalize)
export.denomination = getTyped(Denomination.Denomination)
export.itsToken = getTyped(Token.ItsToken)
export.symbol = getTyped(Token.Symbol)
export.contractAddress = getTyped(Token.ContractAddress)
export.decimal = getTyped(Token.Decimal)
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) }
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) }
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.IdCard) }
export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMask.IdIssuerCard) }
export.isReusable = getTyped(SettingsMask.IsReusable)
export.useActivation = getTyped(SettingsMask.NeedActivation)
export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge)
export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain)
export.useBlock = getTyped(SettingsMask.UseBlock)
export.oneApdu = getTyped(SettingsMask.OneApdu)
export.useCVC = getTyped(SettingsMask.UseCvc)
export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin)
export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2)
export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin)
export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay)
export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay)
export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated)
export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated)
export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal)
export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData)
export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted)
export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption)
export.useNDEF = getTyped(SettingsMaskNdef.UseNdef)
export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef)
export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef)
export.aar = getTyped(SettingsMaskNdef.Aar)
export.aarCustom = getTyped(SettingsMaskNdef.AarCustom)
export.uri = getTyped(SettingsMaskNdef.Uri)
export.PIN = getTyped(Pins.Pin)
export.PIN2 = getTyped(Pins.Pin2)
export.PIN3 = getTyped(Pins.Pin3)
export.CVC = getTyped(Pins.Cvc)
export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2)
return export
}
private inline fun <reified Type> getTyped(id: Id): Type {
return getTypedBy<Type>(valuesHolder, id)!!
}
private inline fun <reified Type> getTypedBy(holder: ConfigValuesHolder, id: Id): Type? {
Log.d(this, "getTyped for id: $id")
var typedValue = holder.get(id)?.get()
typedValue = when (typedValue) {
is ListViewModel -> typedValue.selectedItem as Type
else -> typedValue as? Type ?: null
}
return typedValue
}
}
class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
private val valuesHolder = ConfigValuesHolder()
private val itemTypes = ItemTypes()
override fun convert(from: PersonalizationConfig): List<Item> {
valuesHolder.init(from)
val blocList = mutableListOf<Item>()
blocList.add(cardNumber())
blocList.add(common())
blocList.add(signingMethod())
blocList.add(signHashExProperties())
blocList.add(denomination())
blocList.add(token())
blocList.add(productMask())
blocList.add(settingsMask())
blocList.add(settingsMaskProtocolEnc())
blocList.add(settingsMaskNdef())
blocList.add(pins())
blocList.iterate {
if (itemTypes.hiddenList.contains(it.id)) {
it.viewModel.viewState.isHidden = true
}
}
return blocList
}
private fun cardNumber(): ItemGroup {
val block = createGroup(BlockId.CardNumber)
mutableListOf(
CardNumber.Series,
CardNumber.Number,
CardNumber.BatchId
).forEach { createItem(block, it as Id) }
return block
}
private fun common(): ItemGroup {
val block = createGroup(BlockId.Common)
mutableListOf(
Common.Curve,
Common.Blockchain,
Common.BlockchainCustom,
Common.MaxSignatures,
Common.CreateWallet
).forEach { createItem(block, it) }
return block
}
private fun signingMethod(): ItemGroup {
val block = createGroup(BlockId.SigningMethod)
mutableListOf(
SigningMethod.SignTx,
SigningMethod.SignTxRaw,
SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer,
SigningMethod.SignExternal
).forEach { createItem(block, it) }
return block
}
private fun signHashExProperties(): ItemGroup {
val block = createGroup(BlockId.SignHashExProp)
mutableListOf(
SignHashExProp.PinLessFloorLimit,
SignHashExProp.CryptoExKey,
SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig,
SignHashExProp.CheckPin3
).forEach { createItem(block, it) }
return block
}
private fun denomination(): ItemGroup {
val block = createGroup(BlockId.Denomination)
mutableListOf(
Denomination.WriteOnPersonalize,
Denomination.Denomination
).forEach { createItem(block, it) }
return block
}
private fun token(): ItemGroup {
val block = createGroup(BlockId.Token)
mutableListOf(
Token.ItsToken,
Token.Symbol,
Token.ContractAddress,
Token.Decimal
).forEach { createItem(block, it) }
return block
}
private fun productMask(): ItemGroup {
val block = createGroup(BlockId.ProdMask)
mutableListOf(
ProductMask.Note,
ProductMask.Tag,
ProductMask.IdCard,
ProductMask.IdIssuerCard
).forEach { createItem(block, it) }
return block
}
private fun settingsMask(): ItemGroup {
val block = createGroup(BlockId.SettingsMask)
mutableListOf(
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
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskProtocolEnc(): ItemGroup {
val block = createGroup(BlockId.SettingsMaskProtocolEnc)
mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted,
SettingsMaskProtocolEnc.AllowStaticEncryption
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskNdef(): ItemGroup {
val block = createGroup(BlockId.SettingsMaskNdef)
mutableListOf(
SettingsMaskNdef.UseNdef,
SettingsMaskNdef.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar,
SettingsMaskNdef.AarCustom,
SettingsMaskNdef.Uri
).forEach { createItem(block, it) }
return block
}
private fun pins(): ItemGroup {
val block = createGroup(BlockId.Pins)
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
.forEach { createItem(block, it) }
return block
}
private fun createGroup(id: Id): ItemGroup {
return SimpleItemGroup(id).apply { addItem(TextItem(id)) }
}
private fun createItem(itemGroup: ItemGroup, id: Id) {
val holder = valuesHolder.get(id) ?: return
val item = when {
itemTypes.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
itemTypes.listItemList.contains(id) -> SpinnerItem(id, holder.list as List<KeyValue>, holder.get())
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> SimpleItemGroup(Additional.UNDEFINED)
}
itemGroup.addItem(item)
}
}

View file

@ -0,0 +1,113 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.commands.CardData
import com.tangem.commands.EllipticCurve
import com.tangem.commands.ProductMaskBuilder
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.commands.personalization.entities.NdefRecord
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
import java.util.*
class PersonalizationConfigToCardConfig : Converter<PersonalizationConfig, CardConfig> {
override fun convert(from: PersonalizationConfig): CardConfig {
val signingMethod = com.tangem.commands.SigningMethod.build(
signHash = from.SigningMethod0,
signRaw = from.SigningMethod1,
signHashValidatedByIssuer = from.SigningMethod2,
signRawValidatedByIssuer = from.SigningMethod3,
signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4,
signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5,
signPos = from.SigningMethod6
)
val isNote = from.cardData.product_note
val isTag = from.cardData.product_tag
val isIdCard = from.cardData.product_id_card
val isIdIssuer = from.cardData.product_id_issuer
val productMaskBuilder = ProductMaskBuilder()
if (isNote) productMaskBuilder.add(com.tangem.commands.ProductMask.note)
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
if (isIdIssuer) productMaskBuilder.add(com.tangem.commands.ProductMask.idIssuer)
val productMask = productMaskBuilder.build()
var tokenSymbol: String? = null
var tokenContractAddress: String? = null
var tokenDecimal: Int? = null
if (from.itsToken) {
tokenSymbol = from.symbol
tokenContractAddress = from.contractAddress
tokenDecimal = from.decimal.toInt()
}
val blockchain = if (from.blockchain.isNotEmpty()) from.blockchain else from.blockchainCustom
val cardData = CardData(
blockchainName = blockchain,
batchId = from.batchId,
productMask = productMask,
tokenSymbol = tokenSymbol,
tokenContractAddress = tokenContractAddress,
tokenDecimal = tokenDecimal,
issuerName = null,
manufactureDateTime = Calendar.getInstance().time,
manufacturerSignature = null)
val ndefs = mutableListOf<NdefRecord>()
if (from.uri.isNotEmpty()) {
ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri))
}
when (from.aar) {
"None" -> null
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
}?.let { ndefs.add(it) }
return CardConfig(
"Tangem",
"Tangem Test",
from.series,
from.startNumber,
1000,
from.PIN,
from.PIN2,
from.PIN3,
from.hexCrExKey,
from.CVC,
from.pauseBeforePIN2.toInt(),
from.smartSecurityDelay,
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
from.MaxSignatures.toInt(),
from.isReusable,
from.allowSwapPIN,
from.allowSwapPIN2,
from.useActivation,
from.useCVC,
from.useNDEF,
from.useDynamicNDEF,
from.oneApdu,
from.useBlock,
from.allowSelectBlockchain,
from.forbidPurgeWallet,
from.protocolAllowUnencrypted,
from.protocolAllowStaticEncryption,
from.protectIssuerDataAgainstReplay,
from.forbidDefaultPIN,
from.disablePrecomputedNDEF,
from.skipSecurityDelayIfValidatedByIssuer,
from.skipCheckPIN2andCVCIfValidatedByIssuer,
from.skipSecurityDelayIfValidatedByLinkedTerminal,
from.restrictOverwriteIssuerDataEx,
from.requireTerminalTxSignature,
from.requireTerminalCertSignature,
from.checkPIN3onCard,
from.createWallet,
cardData,
ndefs
)
}
}

View file

@ -1,26 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
import com.tangem.tangemtest._arch.structure.abstraction.ModelConverter
import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.ItemsToPersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToItem
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigConverter : ModelConverter<PersonalizeConfig> {
private val toModel: ItemsToModel<PersonalizeConfig> = ItemsToPersonalizeConfig()
private val toItems: ModelToItems<PersonalizeConfig> = PersonalizeConfigToItem()
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
return toModel.convert(from, default)
}
override fun convert(from: PersonalizeConfig): List<Item> {
return toItems.convert(from)
}
}

View file

@ -1,118 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
import com.tangem.tangemtest._arch.structure.impl.KeyValue
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class ItemsToPersonalizeConfig : ItemsToModel<PersonalizeConfig> {
protected val valuesHolder = PersonalizeConfigValuesHolder()
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
valuesHolder.init(default)
mapListItems(from)
return createModel()
}
private fun mapListItems(itemList: List<Item>) {
itemList.forEach { item -> mapItemToHolder(item) }
}
private fun mapItemToHolder(item: Item) {
when (item) {
is Block -> mapListItems(item.itemList)
is BaseItem<*> -> {
val defValue = valuesHolder.get(item.id) ?: return
defValue.set(item.viewModel.data)
}
}
}
private fun createModel(): PersonalizeConfig {
val export = PersonalizeConfig()
export.series = getTyped(CardNumber.Series)
export.startNumber = getTyped(CardNumber.Number)
export.curveID = getTyped(Common.Curve)
export.blockchain = getTyped(Common.Blockchain)
export.blockchainCustom = getTyped(Common.BlockchainCustom)
export.MaxSignatures = getTyped(Common.MaxSignatures)
export.createWallet = getTyped(Common.CreateWallet)
export.SigningMethod0 = getTyped(SigningMethod.SignTx)
export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw)
export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx)
export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw)
export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer)
export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer)
export.SigningMethod6 = getTyped(SigningMethod.SignExternal)
export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit)
export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey)
export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig)
export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig)
export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3)
export.writeOnPersonalization = getTyped(Denomination.WriteOnPersonalize)
export.denomination = getTyped(Denomination.Denomination)
export.itsToken = getTyped(Token.ItsToken)
export.symbol = getTyped(Token.Symbol)
export.contractAddress = getTyped(Token.ContractAddress)
export.decimal = getTyped(Token.Decimal)
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) }
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) }
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.IdCard) }
export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMask.IdIssuerCard) }
export.isReusable = getTyped(SettingsMask.IsReusable)
export.useActivation = getTyped(SettingsMask.NeedActivation)
export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge)
export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain)
export.useBlock = getTyped(SettingsMask.UseBlock)
export.oneApdu = getTyped(SettingsMask.OneApdu)
export.useCVC = getTyped(SettingsMask.UseCvc)
export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin)
export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2)
export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin)
export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay)
export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay)
export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated)
export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated)
export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal)
export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData)
export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted)
export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption)
export.useNDEF = getTyped(SettingsMaskNdef.UseNdef)
export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef)
export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef)
export.aar = getTyped(SettingsMaskNdef.Aar)
export.aarCustom = getTyped(SettingsMaskNdef.AarCustom)
export.uri = getTyped(SettingsMaskNdef.Uri)
export.PIN = getTyped(Pins.Pin)
export.PIN2 = getTyped(Pins.Pin2)
export.PIN3 = getTyped(Pins.Pin3)
export.CVC = getTyped(Pins.Cvc)
export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2)
return export
}
private inline fun <reified Type> getTyped(id: Id): Type {
return getTypedBy<Type>(valuesHolder, id)!!
}
private inline fun <reified Type> getTypedBy(holder: PersonalizeConfigValuesHolder, id: Id): Type? {
Log.d(this, "getTyped for id: $id")
var typedValue = holder.get(id)?.get()
typedValue = when (typedValue) {
is ListValueWrapper -> (typedValue.selectedItem as KeyValue).value as Type
else -> typedValue as? Type ?: null
}
return typedValue
}
}

View file

@ -1,305 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
import com.tangem.commands.CardData
import com.tangem.commands.EllipticCurve
import com.tangem.commands.ProductMaskBuilder
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.commands.personalization.entities.NdefRecord
import com.tangem.tangemtest._arch.structure.Additional
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.*
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
import java.util.*
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigToItem : ModelToItems<PersonalizeConfig> {
private val valuesHolder = PersonalizeConfigValuesHolder()
private val itemTypes = ItemTypes()
override fun convert(from: PersonalizeConfig): List<Item> {
valuesHolder.init(from)
val blocList = mutableListOf<Item>()
blocList.add(cardNumber())
blocList.add(common())
blocList.add(signingMethod())
blocList.add(signHashExProperties())
blocList.add(denomination())
blocList.add(token())
blocList.add(productMask())
blocList.add(settingsMask())
blocList.add(settingsMaskProtocolEnc())
blocList.add(settingsMaskNdef())
blocList.add(pins())
val payloadBlock = ListItemBlock(Additional.JSON_TAILS)
addPayload(payloadBlock, from)
blocList.add(payloadBlock)
blocList.iterate {
if (itemTypes.hiddenList.contains(it.id)) {
(it as BaseItem<*>)?.viewModel.viewState.isHiddenField = true
}
}
return blocList
}
private fun cardNumber(): Block {
val block = createBlock(BlockId.CardNumber)
mutableListOf(
CardNumber.Series,
CardNumber.Number,
CardNumber.BatchId
).forEach { createItem(block, it as Id) }
return block
}
private fun common(): Block {
val block = createBlock(BlockId.Common)
mutableListOf(
Common.Curve,
Common.Blockchain,
Common.BlockchainCustom,
Common.MaxSignatures,
Common.CreateWallet
).forEach { createItem(block, it) }
return block
}
private fun signingMethod(): Block {
val block = createBlock(BlockId.SigningMethod)
mutableListOf(
SigningMethod.SignTx,
SigningMethod.SignTxRaw,
SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer,
SigningMethod.SignExternal
).forEach { createItem(block, it) }
return block
}
private fun signHashExProperties(): Block {
val block = createBlock(BlockId.SignHashExProp)
mutableListOf(
SignHashExProp.PinLessFloorLimit,
SignHashExProp.CryptoExKey,
SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig,
SignHashExProp.CheckPin3
).forEach { createItem(block, it) }
return block
}
private fun denomination(): Block {
val block = createBlock(BlockId.Denomination)
mutableListOf(
Denomination.WriteOnPersonalize,
Denomination.Denomination
).forEach { createItem(block, it) }
return block
}
private fun token(): Block {
val block = createBlock(BlockId.Token)
mutableListOf(
Token.ItsToken,
Token.Symbol,
Token.ContractAddress,
Token.Decimal
).forEach { createItem(block, it) }
return block
}
private fun productMask(): Block {
val block = createBlock(BlockId.ProdMask)
mutableListOf(
ProductMask.Note,
ProductMask.Tag,
ProductMask.IdCard,
ProductMask.IdIssuerCard
).forEach { createItem(block, it) }
return block
}
private fun settingsMask(): Block {
val block = createBlock(BlockId.SettingsMask)
mutableListOf(
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
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskProtocolEnc(): Block {
val block = createBlock(BlockId.SettingsMaskProtocolEnc)
mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted,
SettingsMaskProtocolEnc.AllowStaticEncryption
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskNdef(): Block {
val block = createBlock(BlockId.SettingsMaskNdef)
mutableListOf(
SettingsMaskNdef.UseNdef,
SettingsMaskNdef.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar,
SettingsMaskNdef.AarCustom,
SettingsMaskNdef.Uri
).forEach { createItem(block, it) }
return block
}
private fun pins(): Block {
val block = createBlock(BlockId.Pins)
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
.forEach { createItem(block, it) }
return block
}
private fun createBlock(id: Id): ListItemBlock {
return ListItemBlock(id).apply { addItem(TextItem(id)) }
}
private fun addPayload(block: Block, from: PersonalizeConfig) {
block.payload[PayloadKey.incomingJson] = from
}
private fun createItem(block: ListItemBlock, id: Id) {
val holder = valuesHolder.get(id) ?: return
val item = when {
itemTypes.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
itemTypes.listItemList.contains(id) -> ListItem(id, holder.list as List<KeyValue>, holder.get())
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> ListItemBlock(Additional.UNDEFINED)
}
block.addItem(item)
}
}
class PersonalizeConfigToCardConfig : Converter<PersonalizeConfig, CardConfig> {
override fun convert(from: PersonalizeConfig): CardConfig {
val signingMethod = com.tangem.commands.SigningMethod.build(
signHash = from.SigningMethod0,
signRaw = from.SigningMethod1,
signHashValidatedByIssuer = from.SigningMethod2,
signRawValidatedByIssuer = from.SigningMethod3,
signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4,
signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5,
signPos = from.SigningMethod6
)
val isNote = from.cardData.product_note
val isTag = from.cardData.product_tag
val isIdCard = from.cardData.product_id_card
val isIdIssuer = from.cardData.product_id_issuer
val productMaskBuilder = ProductMaskBuilder()
if (isNote) productMaskBuilder.add(com.tangem.commands.ProductMask.note)
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
if (isIdIssuer) productMaskBuilder.add(com.tangem.commands.ProductMask.idIssuer)
val productMask = productMaskBuilder.build()
var tokenSymbol: String? = null
var tokenContractAddress: String? = null
var tokenDecimal: Int? = null
if (from.itsToken) {
tokenSymbol = from.symbol
tokenContractAddress = from.contractAddress
tokenDecimal = from.decimal.toInt()
}
val blockchain = if (from.blockchain.isNotEmpty()) from.blockchain else from.blockchainCustom
val cardData = CardData(
blockchainName = blockchain,
batchId = from.batchId,
productMask = productMask,
tokenSymbol = tokenSymbol,
tokenContractAddress = tokenContractAddress,
tokenDecimal = tokenDecimal,
issuerName = null,
manufactureDateTime = Calendar.getInstance().time,
manufacturerSignature = null)
val ndefs = mutableListOf<NdefRecord>()
if (from.uri.isNotEmpty()) {
ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri))
}
when (from.aar) {
"None" -> null
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
}?.let { ndefs.add(it) }
return CardConfig(
"Tangem",
"Tangem Test",
from.series,
from.startNumber,
1000,
from.PIN,
from.PIN2,
from.PIN3,
from.hexCrExKey,
from.CVC,
from.pauseBeforePIN2.toInt(),
from.smartSecurityDelay,
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
from.MaxSignatures.toInt(),
from.isReusable,
from.allowSwapPIN,
from.allowSwapPIN2,
from.useActivation,
from.useCVC,
from.useNDEF,
from.useDynamicNDEF,
from.oneApdu,
from.useBlock,
from.allowSelectBlockchain,
from.forbidPurgeWallet,
from.protocolAllowUnencrypted,
from.protocolAllowStaticEncryption,
from.protectIssuerDataAgainstReplay,
from.forbidDefaultPIN,
from.disablePrecomputedNDEF,
from.skipSecurityDelayIfValidatedByIssuer,
from.skipCheckPIN2andCVCIfValidatedByIssuer,
from.skipSecurityDelayIfValidatedByLinkedTerminal,
from.restrictOverwriteIssuerDataEx,
from.requireTerminalTxSignature,
from.requireTerminalCertSignature,
from.checkPIN3onCard,
from.createWallet,
cardData,
ndefs
)
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.commands.personalization.entities.Manufacturer
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface DefaultPersonalizeParams { interface DefaultPersonalizationParams {
companion object { companion object {
fun issuer(): Issuer { fun issuer(): Issuer {

View file

@ -3,7 +3,7 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class PersonalizeConfig { class PersonalizationConfig {
// Card number // Card number
var series = "BB" var series = "BB"

View file

@ -0,0 +1,78 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.widget.WidgetBuilder
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.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.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizationFragment : BaseCardActionFragment() {
override val itemsManager: ItemsManager by lazy { PersonalizationItemsManager(PersonalizationConfigStore(requireContext())) }
override fun getLayoutId(): Int = R.layout.fg_personalization
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(itemsManager as PersonalizationItemsManager)
}
override fun initFab() {
actionFab.setOnClickListener { actionVM.invokeMainAction() }
}
override fun createWidgets(widgetCreatedCallback: () -> Unit) {
Log.d(this, "createWidgets")
val itemList = mutableListOf<Item>()
actionVM.ldItemList.observe(viewLifecycleOwner, Observer { list ->
Log.d(this, "ldBlockList size: ${list.size}")
itemList.clear()
itemList.addAll(list)
itemList.forEach { WidgetBuilder(PersonalizationItemBuilder()).build(it, itemContainer) }
actionVM.attachToPayload(mutableMapOf(
PayloadKey.actionView to this as ActionView,
PayloadKey.itemList to itemList
))
widgetCreatedCallback()
})
}
override fun subscribeToViewModelChanges() {
Log.d(this, "subscribeToViewModelChanges")
listenEvent()
listenError()
listenDescriptionSwitchChanges()
}
override fun listenEvent() {
actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer {
mainActivityVM.changeResponseEvent(it)
navigateTo(R.id.action_nav_card_action_to_response_screen)
})
}
override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) {
super.showSnackbar(id) {
when (id) {
ItemError.BadSeries -> R.string.card_error_bad_series
ItemError.BadCardNumber -> R.string.card_error_bad_series_number
else -> additionalHandler?.invoke(id) ?: UNDEFINED
}
}
}
}

View file

@ -1,116 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._main.MainViewModel
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError
import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.ui.ActionViewModelFactory
import com.tangem.tangemtest.ucase.ui.ParamsViewModel
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.WidgetBuilder
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizeFragment : Fragment(), ActionView {
private val mainActivityVM: MainViewModel by activityViewModels()
private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) }
private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! }
private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) }
private lateinit var mainView: View
private val blockContainer: ViewGroup by lazy { mainView.findViewById<LinearLayout>(R.id.ll_container) }
private val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(this, "onCreateView")
mainView = inflater.inflate(R.layout.fg_personalize, container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val blockList = mutableListOf<Item>()
personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list ->
blockList.clear()
blockList.addAll(list)
blockList.forEach { WidgetBuilder().build(it, blockContainer) }
paramsVM.attachToPayload(mutableMapOf(
PayloadKey.actionView to this as ActionView,
PayloadKey.itemList to blockList
))
})
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
personalizeVM.toggleDescriptionVisibility(it)
})
val cardManager = TangemSdk.init(requireActivity())
paramsVM.setCardManager(cardManager)
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer {
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
showSnackbarMessage(it)
})
paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbarMessage(it) })
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
}
protected fun showSnackbarMessage(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
override fun showActionFab(show: Boolean) {
if (show) actionFab.show() else actionFab.hide()
}
override fun showSnackbar(id: Id) {
// MainResourceHolder.safeGet<>()
when (id) {
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
ItemError.BadSeries -> showSnackbar(R.string.card_error_bad_series)
ItemError.BadCardNumber -> showSnackbar(R.string.card_error_bad_series_number)
else -> showSnackbar(requireContext().getString(R.string.unknown))
}
}
override fun showSnackbar(id: Int) {
showSnackbar(requireContext().getString(id))
}
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
}

View file

@ -1,32 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.iterate
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizeViewModelFactory(private val config: PersonalizeConfig) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(config) as T
}
class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() {
val ldBlockList: MutableLiveData<List<Item>> by lazy { MutableLiveData(initBlockList()) }
private fun initBlockList(): List<Item> = PersonalizeConfigConverter().convert(config)
fun toggleDescriptionVisibility(state: Boolean) {
ldBlockList.value?.iterate {
val baseItem = it as? BaseItem<*> ?: return@iterate
baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.StringId
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
import com.tangem.tangemtest.ucase.resources.Resources
import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom
/**
[REDACTED_AUTHOR]
*/
abstract class BaseAppWidget(parent: ViewGroup, item: Item) : BaseViewWidget(parent, item) {
override fun getName(): String {
val idName = if (item.id is StringId) (item.id as StringId).value else ""
val resources = getResources() ?: return idName
return view.stringFrom(resources.resName)
}
}
fun ViewWidget.getResources(): Resources? = MainResourceHolder.get(item.id)
fun ViewWidget.getResNameId(): Int = MainResourceHolder.safeGet<Resources>(item.id).resName
fun ViewWidget.getResDescriptionId(): Int? = MainResourceHolder.get(item.id)?.resDescription

View file

@ -1,38 +1,36 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.transition.AutoTransition import androidx.transition.AutoTransition
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseWidget
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResDescription
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
abstract class DescriptionWidget<D>( abstract class DescriptionWidget(
parent: ViewGroup, parent: ViewGroup,
data: BaseItem<D> item: Item
) : BaseWidget<D>(parent, data) { ) : BaseAppWidget(parent, item) {
private val descriptionContainer: ViewGroup by lazy { view.findViewById<ViewGroup>(R.id.container_description) } 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) } private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
init { init {
Log.d(this, "init id: ${dataItem.id}") Log.d(this, "init id: ${item.id}")
initDescriptionWidget() initDescriptionWidget()
} }
private fun initDescriptionWidget() { private fun initDescriptionWidget() {
dataItem.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) } item.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) }
} }
protected fun changeDescriptionVisibility(state: Int) { protected open fun changeDescriptionVisibility(state: Int) {
val tv = tvDescription ?: return val tv = tvDescription ?: return
val descriptionId = getResDescription() ?: return val descriptionId = getResDescriptionId() ?: return
val description = tv.context.getString(descriptionId) val description = tv.context.getString(descriptionId)
if (description.isEmpty()) return if (description.isEmpty()) return
@ -41,14 +39,3 @@ abstract class DescriptionWidget<D>(
descriptionContainer.visibility = state descriptionContainer.visibility = state
} }
} }

View file

@ -1,4 +1,4 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -7,14 +7,17 @@ import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.EditTextItem import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<String>(parent, data) { class EditTextWidget(
parent: ViewGroup,
private val typedItem: EditTextItem
) : DescriptionWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_edit_text override fun getLayoutId(): Int = R.layout.w_personalize_item_edit_text
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item) private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
@ -22,7 +25,7 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<
private val watcher = object : TextWatcher { private val watcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
dataItem.viewModel.updateDataByView(stringOf(s)) typedItem.viewModel.updateDataByView(stringOf(s))
} }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
@ -30,10 +33,10 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<
} }
init { init {
tilItem.hint = tilItem.context.getString(getResNameId()) tilItem.hint = getName()
etItem.setText(dataItem.viewModel.data) etItem.setText(typedItem.getTypedData())
etItem.addTextChangedListener(watcher) etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = { silentUpdate(it) } item.viewModel.onDataUpdated = { silentUpdate(it as? String) }
} }
private fun silentUpdate(value: String?) { private fun silentUpdate(value: String?) {

View file

@ -1,20 +1,19 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.TextItem import com.tangem.tangemtest._arch.structure.impl.TextItem
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class TextWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget<String>(parent, data) { class GroupTitleWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(parent, data) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_text override fun getLayoutId(): Int = R.layout.w_personalize_item_text
private val tvName = view.findViewById<TextView>(R.id.tv_name) private val tvName = view.findViewById<TextView>(R.id.tv_name)
init { init {
tvName?.setText(getResNameId()) tvName.text = getName()
} }
} }

View file

@ -1,4 +1,4 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.text.Editable import android.text.Editable
import android.text.InputFilter import android.text.InputFilter
@ -9,7 +9,6 @@ import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.NumberItem import com.tangem.tangemtest._arch.structure.impl.NumberItem
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
@ -17,7 +16,11 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Number>(parent, data) { class NumberWidget(
parent: ViewGroup,
private val typedItem: NumberItem
) : DescriptionWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_number override fun getLayoutId(): Int = R.layout.w_personalize_item_number
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item) private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
@ -25,7 +28,7 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
private val watcher = object : TextWatcher { private val watcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
dataItem.viewModel.updateDataByView(getValue(stringOf(s))) typedItem.viewModel.updateDataByView(getValue(stringOf(s)))
} }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
@ -33,14 +36,14 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
} }
init { init {
tilItem.hint = tilItem.context.getString(getResNameId()) tilItem.hint = getName()
//TODO: remove from Widget //TODO: remove from Widget
if (dataItem.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13)) if (item.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13))
etItem.setText(stringOf(dataItem.viewModel.data)) etItem.setText(stringOf(typedItem.getTypedData()))
etItem.addTextChangedListener(watcher) etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = { silentUpdate(it) } item.viewModel.onDataUpdated = { silentUpdate(it as? Number) }
} }
private fun silentUpdate(value: Number?) { private fun silentUpdate(value: Number?) {
@ -53,5 +56,4 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
private fun getValue(value: String): Long { private fun getValue(value: String): Long {
return if (value.isEmpty()) 0L else value.toLong() return if (value.isEmpty()) 0L else value.toLong()
} }
} }

View file

@ -0,0 +1,23 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest._arch.widget.ItemWidgetBuilder
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
/**
[REDACTED_AUTHOR]
*/
class PersonalizationItemBuilder : ItemWidgetBuilder {
override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? {
return when (item) {
is TextItem -> GroupTitleWidget(parent, item)
is EditTextItem -> EditTextWidget(parent, item)
is NumberItem -> NumberWidget(parent, item)
is BoolItem -> SwitchWidget(parent, item)
is SpinnerItem -> SpinnerWidget(parent, item)
else -> null
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -7,50 +7,52 @@ import android.widget.BaseAdapter
import android.widget.Spinner import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.KeyValue import com.tangem.tangemtest._arch.structure.abstraction.KeyValue
import com.tangem.tangemtest._arch.structure.impl.ListItem import com.tangem.tangemtest._arch.structure.abstraction.ListViewModel
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper import com.tangem.tangemtest._arch.structure.impl.SpinnerItem
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget<ListValueWrapper>(parent, listItem) { class SpinnerWidget(
parent: ViewGroup,
typedItem: SpinnerItem
) : DescriptionWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner
private val data: ListValueWrapper = listItem.getData()!! private val viewModel: ListViewModel = typedItem.viewModel as ListViewModel
private val spItem = view.findViewById<Spinner>(R.id.sp_item) private val spinner = view.findViewById<Spinner>(R.id.sp_item)
private val spAdapter = SpItemAdapter(data.itemList) private val spAdapter = SpItemAdapter(viewModel.itemList)
private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener { private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
data.selectedItem = data.itemList[position] viewModel.selectedItem = viewModel.itemList[position].value
dataItem.viewModel.updateDataByView(data) item.viewModel.updateDataByView(viewModel)
} }
} }
init { init {
val name = view.findViewById<TextView>(R.id.tv_name) val name = view.findViewById<TextView>(R.id.tv_name)
name.setText(getResNameId()) name.text = getName()
spItem.adapter = spAdapter spinner.adapter = spAdapter
spAdapter.getItemPosition(stringOf(data.selectedItem))?.let { spAdapter.getItemPosition(stringOf(viewModel.selectedItem))?.let {
spItem.setSelection(it) spinner.setSelection(it)
} }
spItem.onItemSelectedListener = onItemSelectedListener spinner.onItemSelectedListener = onItemSelectedListener
dataItem.viewModel.onDataUpdated = { item.viewModel.onDataUpdated = {
it?.apply { val selectedItem = it as? String
spItem.onItemSelectedListener = null spinner.onItemSelectedListener = null
this.itemList.firstOrNull { item -> item.value == selectedItem }?.let { viewModel.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue ->
val position = itemList.indexOf(it) val position = viewModel.itemList.indexOf(keyValue)
spItem.setSelection(position) spinner.setSelection(position)
}
spItem.onItemSelectedListener = onItemSelectedListener
} }
spinner.onItemSelectedListener = onItemSelectedListener
} }
} }
} }

View file

@ -1,31 +1,34 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.BoolItem import com.tangem.tangemtest._arch.structure.impl.BoolItem
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class SwitchWidget(parent: ViewGroup, data: BoolItem) : DescriptionWidget<Boolean>(parent, data) { class SwitchWidget(
parent: ViewGroup,
private val typedItem: BoolItem
) : DescriptionWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_switch override fun getLayoutId(): Int = R.layout.w_personalize_item_switch
private val switchItem = view.findViewById<SwitchCompat>(R.id.sw_item) private val switchItem = view.findViewById<SwitchCompat>(R.id.sw_item)
private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
dataItem.viewModel.updateDataByView(isChecked) typedItem.viewModel.updateDataByView(isChecked)
} }
init { init {
switchItem.setText(getResNameId()) switchItem.text = getName()
switchItem.isChecked = dataItem.viewModel.data ?: false switchItem.isChecked = typedItem.getTypedData() ?: false
switchItem.setOnCheckedChangeListener(changeListener) switchItem.setOnCheckedChangeListener(changeListener)
dataItem.viewModel.onDataUpdated = { typedItem.viewModel.onDataUpdated = {
switchItem.setOnCheckedChangeListener(null) switchItem.setOnCheckedChangeListener(null)
switchItem.isChecked = it ?: false switchItem.isChecked = it as? Boolean ?: false
switchItem.setOnCheckedChangeListener(changeListener) switchItem.setOnCheckedChangeListener(changeListener)
} }
} }

View file

@ -1,48 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.ViewWidget
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.StubWidget
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block.LinearBlockWidget
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item.*
/**
[REDACTED_AUTHOR]
*/
class WidgetBuilder {
fun build(item: Item, parent: ViewGroup): ViewWidget? {
return when (item) {
is Block -> buildBlock(item, parent)
is BaseItem<*> -> buildUnitItem(item, parent)
else -> StubWidget(parent)
}
}
private fun buildBlock(block: Block, parent: ViewGroup): ViewWidget {
return when (block) {
is ListItemBlock -> {
val linearBlock = LinearBlockWidget(parent, block)
block.getItems().forEach { build(it, linearBlock.view as ViewGroup) }
linearBlock
}
else -> StubWidget(parent)
}
}
private fun buildUnitItem(item: BaseItem<*>, parent: ViewGroup): ViewWidget? {
return when (item) {
is TextItem -> TextWidget(parent, item)
is EditTextItem -> EditTextWidget(parent, item)
is NumberItem -> NumberWidget(parent, item)
is BoolItem -> SwitchWidget(parent, item)
is ListItem -> SpinnerWidget(parent, item)
else -> null
}
}
}

View file

@ -1,54 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.DataHolder
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
import com.tangem.tangemtest.ucase.resources.Resources
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
/**
[REDACTED_AUTHOR]
*/
interface ViewWidget : LayoutHolder {
val view: View
}
interface DataWidget<D> : ViewWidget {
var dataItem: BaseItem<D>
}
interface BlockViewWidget : ViewWidget, DataHolder<List<ItemViewModel<*>>>
abstract class BaseWidget<D>(
parent: ViewGroup,
override var dataItem: BaseItem<D>
) : ViewWidget, DataWidget<D> {
override val view: View = inflate(getLayoutId(), parent)
init {
if (dataItem.viewModel.viewState.isHiddenField) {
view.visibility = View.GONE
}
}
}
abstract class BaseBlockWidget(parent: ViewGroup) : BlockViewWidget {
override val view: View = inflate(getLayoutId(), parent)
}
fun DataWidget<*>.getResNameId(): Int = MainResourceHolder.safeGet<Resources>(dataItem.id).resName
fun DataWidget<*>.getResDescription(): Int? = MainResourceHolder.safeGet<Resources>(dataItem.id).resDescription
internal fun inflate(id: Int, parent: ViewGroup): View {
val layoutId = if (id <= 0) R.layout.w_empty else id
val inflatedView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
parent.addView(inflatedView)
return inflatedView
}

View file

@ -1,14 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget
/**
[REDACTED_AUTHOR]
*/
class StubWidget(parent: ViewGroup) : BaseBlockWidget(parent) {
override fun getLayoutId(): Int = R.layout.w_empty
override var viewModel: List<ItemViewModel<*>> = listOf()
}

View file

@ -1,21 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget
/**
[REDACTED_AUTHOR]
*/
class LinearBlockWidget(
parent: ViewGroup,
private val children: ListItemBlock
) : BaseBlockWidget(parent) {
override fun getLayoutId(): Int = R.layout.w_personilize_block
override var viewModel: List<ItemViewModel<*>> = listOf()
get() = children.itemList.map { it as BaseItem<*> }.map { it.viewModel }
}

View file

@ -0,0 +1,44 @@
package com.tangem.tangemtest.ucase.variants.responses
import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
interface ResponseId : Id
enum class CardId : ResponseId {
cardId,
manufacturerName,
status,
firmwareVersion,
cardPublicKey,
settingsMask,
cardData,
issuerPublicKey,
curve,
maxSignatures,
signingMethod,
pauseBeforePin2,
walletPublicKey,
walletRemainingSignatures,
walletSignedHashes,
health,
isActivated,
activationSeed,
paymentFlowVersion,
userCounter,
userProtectedCounter,
}
enum class CardDataId : ResponseId {
batchId,
manufactureDateTime,
issuerName,
blockchainName,
manufacturerSignature,
productMask,
tokenSymbol,
tokenContractAddress,
tokenDecimal,
}

View file

@ -0,0 +1,56 @@
package com.tangem.tangemtest.ucase.variants.responses
import android.view.View
import androidx.lifecycle.ViewModel
import com.tangem.commands.Card
import com.tangem.commands.SignResponse
import com.tangem.commands.personalization.DepersonalizeResponse
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems
import com.tangem.tangemtest._arch.structure.abstraction.iterate
import com.tangem.tangemtest.ucase.variants.responses.converter.ConvertersStore
import com.tangem.tasks.ScanEvent
import com.tangem.tasks.TaskEvent
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class ResponseViewModel : ViewModel() {
private val convertersHolder = ConvertersStore()
private var itemList: List<Item>? = null
fun createItemList(taskEvent: TaskEvent<*>?): List<Item> {
Log.d(this, "createItemList: itemList size: ${itemList?.size ?: 0}")
val event = taskEvent as? TaskEvent.Event<Any> ?: return emptyList()
val type = event.data::class.java
val converter = convertersHolder.get(type) as? ModelToItems<Any> ?: return emptyList()
itemList = converter.convert(event.data)
return itemList!!
}
fun toggleDescriptionVisibility(state: Boolean) {
itemList?.iterate {
it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
}
fun determineTitleId(taskEvent: TaskEvent<*>?): Int {
val event = taskEvent as? TaskEvent.Event<Any> ?: return R.string.unknown
return when (event.data) {
is ScanEvent.OnReadEvent -> R.string.fg_name_response_scan
is SignResponse -> R.string.fg_name_response_sign
is Card -> R.string.fg_name_response_personalization
is DepersonalizeResponse -> R.string.fg_name_response_depersonalization
else -> R.string.unknown
}
}
}

View file

@ -0,0 +1,105 @@
package com.tangem.tangemtest.ucase.variants.responses.converter
import com.tangem.commands.Card
import com.tangem.commands.CardData
import com.tangem.commands.Settings
import com.tangem.commands.SettingsMask
import com.tangem.common.extensions.toHexString
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.*
import com.tangem.tangemtest._arch.structure.impl.BoolItem
import com.tangem.tangemtest._arch.structure.impl.TextItem
import com.tangem.tangemtest.ucase.variants.personalize.BlockId
import com.tangem.tangemtest.ucase.variants.responses.CardDataId
import com.tangem.tangemtest.ucase.variants.responses.CardId
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class CardConverter : ModelToItems<Card> {
override fun convert(from: Card): List<Item> {
val itemList = mutableListOf<Item>()
// val holder = GsonInitializer()
// itemList.add(TextItem(Additional.JSON_INCOMING, holder.gson.toJson(from)))
itemList.add(simpleFields(from))
itemList.add(cardData(from.cardData))
itemList.add(settingsMask(from.settingsMask))
hideEmptyNullFields(itemList)
return itemList
}
private fun hideEmptyNullFields(itemList: MutableList<Item>) {
itemList.iterate {
val data = it.getData<Any?>()
val isHidden = if (data == null) true
else when (data) {
is String -> data.isEmpty()
else -> false
}
if (isHidden) it.viewModel.viewState.isHidden = true
}
}
private fun simpleFields(from: Card): Item {
val group = createGroup(BlockId.Common)
group.addItem(TextItem(CardId.cardId, from.cardId))
group.addItem(TextItem(CardId.manufacturerName, from.manufacturerName))
group.addItem(TextItem(CardId.status, stringOf(from.status)))
group.addItem(TextItem(CardId.firmwareVersion, from.firmwareVersion))
group.addItem(TextItem(CardId.cardPublicKey, from.cardPublicKey?.toHexString()))
group.addItem(TextItem(CardId.issuerPublicKey, from.issuerPublicKey?.toHexString()))
group.addItem(TextItem(CardId.curve, stringOf(from.curve)))
group.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures)))
group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod?.rawValue)))
group.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2)))
group.addItem(TextItem(CardId.walletPublicKey, stringOf(from.walletPublicKey)))
group.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures)))
group.addItem(TextItem(CardId.walletSignedHashes, stringOf(from.walletSignedHashes)))
group.addItem(TextItem(CardId.health, stringOf(from.health)))
group.addItem(TextItem(CardId.isActivated, stringOf(from.isActivated)))
group.addItem(TextItem(CardId.activationSeed, stringOf(from.activationSeed)))
group.addItem(TextItem(CardId.paymentFlowVersion, stringOf(from.paymentFlowVersion)))
group.addItem(TextItem(CardId.userCounter, stringOf(from.userCounter)))
// block.addItem(TextItem(CardId.UserProtectedCounter, stringOf(from.userProtectedCounter)))
return group
}
private fun cardData(from: CardData?): Item {
val group = createGroup(BlockId.Common, R.color.group_card_data)
val data = from ?: return group
// group.addItem(TextItem(StringResId(R.string.response_card_card_data)))
group.addItem(TextItem(CardDataId.batchId, data.batchId))
// Format: Year (2 bytes) | Month (1 byte) | Day (1 byte)
group.addItem(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime)))
group.addItem(TextItem(CardDataId.issuerName, data.issuerName))
group.addItem(TextItem(CardDataId.blockchainName, data.blockchainName))
group.addItem(TextItem(CardDataId.manufacturerSignature, data.manufacturerSignature?.toHexString()))
group.addItem(TextItem(CardDataId.productMask, stringOf(data.productMask?.rawValue)))
group.addItem(TextItem(CardDataId.tokenSymbol, data.tokenSymbol))
group.addItem(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress))
group.addItem(TextItem(CardDataId.tokenDecimal, data.tokenSymbol))
return group
}
private fun settingsMask(from: SettingsMask?): Item {
val group = createGroup(CardId.settingsMask, R.color.group_signing_method)
val data = from ?: return group
// group.addItem(TextItem(StringResId(R.string.response_card_settings_mask)))
Settings.values().forEach { group.addItem(BoolItem(StringId(it.name), data.contains(it))) }
return group
}
private fun createGroup(id: Id, colorId: Int? = null): ItemGroup {
return if (colorId == null) SimpleItemGroup(id)
else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(backgroundColor = colorId)))
}
}

View file

@ -0,0 +1,17 @@
package com.tangem.tangemtest.ucase.variants.responses.converter
import com.tangem.commands.Card
import com.tangem.commands.SignResponse
import com.tangem.commands.personalization.DepersonalizeResponse
import com.tangem.tasks.ScanEvent
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
import java.lang.reflect.Type
class ConvertersStore : BaseTypedHolder<Type, Any>() {
init {
register(ScanEvent.OnReadEvent::class.java, ReadEventConverter())
register(SignResponse::class.java, SignResponseConverter())
register(Card::class.java, CardConverter())
register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter())
}
}

View file

@ -0,0 +1,35 @@
package com.tangem.tangemtest.ucase.variants.responses.converter
import com.tangem.commands.SignResponse
import com.tangem.commands.personalization.DepersonalizeResponse
import com.tangem.tangemtest._arch.structure.StringId
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems
import com.tangem.tangemtest._arch.structure.impl.TextItem
import com.tangem.tasks.ScanEvent
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class ReadEventConverter : ModelToItems<ScanEvent.OnReadEvent> {
override fun convert(from: ScanEvent.OnReadEvent): List<Item> = CardConverter().convert(from.card)
}
class SignResponseConverter : ModelToItems<SignResponse> {
override fun convert(from: SignResponse): List<Item> {
return listOf(
TextItem(StringId("CID"), from.cardId),
TextItem(StringId("Wallet signed hashes"), stringOf(from.walletSignedHashes)),
TextItem(StringId("Wallet remaining signatures"), stringOf(from.walletRemainingSignatures)),
TextItem(StringId("Signature"), stringOf(from.signature))
)
}
}
class DepersonalizeResponseConverter : ModelToItems<DepersonalizeResponse> {
override fun convert(from: DepersonalizeResponse): List<Item> {
return listOf(TextItem(StringId("Is success"), stringOf(from.success)))
}
}

View file

@ -0,0 +1,75 @@
package com.tangem.tangemtest.ucase.variants.responses.ui
import android.os.Bundle
import android.view.*
import android.widget.LinearLayout
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.widget.WidgetBuilder
import com.tangem.tangemtest._main.MainViewModel
import com.tangem.tangemtest.extensions.shareText
import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter
import com.tangem.tangemtest.ucase.ui.BaseFragment
import com.tangem.tangemtest.ucase.variants.responses.ResponseViewModel
import com.tangem.tangemtest.ucase.variants.responses.ui.widget.ResponseItemBuilder
/**
[REDACTED_AUTHOR]
*/
open class ResponseFragment : BaseFragment() {
private val mainActivityVM: MainViewModel by activityViewModels()
private val selfVM: ResponseViewModel by viewModels()
private val itemContainer: ViewGroup by lazy { mainView.findViewById<LinearLayout>(R.id.ll_container) }
override fun getLayoutId(): Int = R.layout.fg_card_response
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTittle()
}
private fun setTittle() {
val titleId = selfVM.determineTitleId(mainActivityVM.responseEvent)
activity?.setTitle(titleId)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
buildWidgets()
listenDescriptionSwitchChanges()
}
private fun buildWidgets() {
val builder = WidgetBuilder(ResponseItemBuilder())
val itemList = selfVM.createItemList(mainActivityVM.responseEvent)
itemList.forEach { builder.build(it, itemContainer) }
}
private fun listenDescriptionSwitchChanges() {
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
selfVM.toggleDescriptionVisibility(it)
})
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
val menuItem = menu.findItem(R.id.action_share)
menuItem.isVisible = true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_share -> {
shareText(ResponseJsonConverter().convertEvent(mainActivityVM.responseEvent))
}
}
return super.onOptionsItemSelected(item)
}
}

View file

@ -0,0 +1,27 @@
package com.tangem.tangemtest.ucase.variants.responses.ui.widget
import android.view.ViewGroup
import com.google.android.material.checkbox.MaterialCheckBox
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.BoolItem
import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom
/**
[REDACTED_AUTHOR]
*/
class CheckBoxWidget(
parent: ViewGroup,
private val typedItem: BoolItem
) : ResponseWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_response_item_checkbox
private val switchItem = view.findViewById<MaterialCheckBox>(R.id.sw_item)
init {
switchItem.text = getName()
switchItem.isChecked = typedItem.getData() ?: false
switchItem.isClickable = false
switchItem.setTextColor(switchItem.colorFrom(R.color.action_name))
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tangemtest.ucase.variants.responses.ui.widget
import android.view.ViewGroup
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.impl.BoolItem
import com.tangem.tangemtest._arch.structure.impl.TextItem
import com.tangem.tangemtest._arch.widget.ItemWidgetBuilder
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
/**
[REDACTED_AUTHOR]
*/
class ResponseItemBuilder : ItemWidgetBuilder {
override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? {
return when (item) {
is TextItem -> ResponseTextWidget(parent, item)
is BoolItem -> CheckBoxWidget(parent, item)
else -> null
}
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.tangemtest.ucase.variants.responses.ui.widget
import android.view.ViewGroup
import android.widget.TextView
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.TextItem
/**
[REDACTED_AUTHOR]
*/
class ResponseTextWidget(
parent: ViewGroup,
private val typedItem: TextItem
) : ResponseWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_response_item
private val tvName: TextView = view.findViewById(R.id.tv_name)
private val tvValue: TextView = view.findViewById(R.id.tv_value)
init {
initWidgets()
}
private fun initWidgets() {
val data = typedItem.getData() as? String
tvName.text = getName()
tvValue.text = data
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.tangemtest.ucase.variants.responses.ui.widget
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget
import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom
import ru.dev.gbixahue.eu4d.lib.android._android.views.toast
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
abstract class ResponseWidget(parent: ViewGroup, item: Item): DescriptionWidget(parent, item) {
init {
view.setOnClickListener {
val data = stringOf(item.getData<Any?>())
val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
?: return@setOnClickListener
val clipboardData = "${getName()} - $data"
val clip: ClipData = ClipData.newPlainText("FieldValue", clipboardData)
clipboard.setPrimaryClip(clip)
val copyMessage = view.stringFrom(R.string.copy_to_clipboard)
view.toast("$copyMessage\n$clipboardData")
}
}
}

View file

@ -2,36 +2,29 @@ package com.tangem.tangemtest.ucase.variants.scan.ui
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.TextView
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
import ru.dev.gbixahue.eu4d.lib.android._android.views.show
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class ScanActionFragment : BaseCardActionFragment() { class ScanActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_action_card_scan override val itemsManager: ItemsManager by lazy { ScanItemsManager() }
override fun getAction(): ActionType = ActionType.Scan override fun getLayoutId(): Int = R.layout.fg_action_card_scan
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
enableActionFab(true)
showActionFab(true)
} }
override fun listenResponse() { override fun listenReadResponse() {
val tvStub = mainView.findViewById<TextView>(R.id.tv_screen_stub) actionVM.seReadResponse.observe(viewLifecycleOwner, Observer {
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) } navigateTo(R.id.action_nav_card_action_to_response_screen)
paramsVM.ldReadResponse.observe(viewLifecycleOwner, Observer {
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
tvStub.show(false)
tvResponse.text = it
}) })
} }
} }

View file

@ -1,7 +1,8 @@
package com.tangem.tangemtest.ucase.variants.sign.ui package com.tangem.tangemtest.ucase.variants.sign.ui
import com.tangem.tangemtest.R import com.tangem.tangemtest.R
import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
/** /**
@ -9,7 +10,7 @@ import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
*/ */
class SignActionFragment : BaseCardActionFragment() { class SignActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_action_card_sign override val itemsManager: ItemsManager by lazy { SignItemsManager() }
override fun getAction(): ActionType = ActionType.Sign override fun getLayoutId(): Int = R.layout.fg_action_card_sign
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -15,7 +15,7 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/ll_incoming_params_container" android:id="@+id/ll_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">

View file

@ -15,7 +15,7 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/ll_incoming_params_container" android:id="@+id/ll_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -15,7 +15,7 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/ll_incoming_params_container" android:id="@+id/ll_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/container_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/def_indent"
android:paddingTop="6dp"
android:paddingEnd="@dimen/def_indent"
android:paddingBottom="8dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/field_content"
android:textSize="16sp"
tools:text="Custom blockchain" />
<TextView
android:id="@+id/tv_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-2dp"
android:textColor="@color/field_info"
android:textSize="13sp"
tools:text="Not Defined" />
<include layout="@layout/w_field_description" />
</LinearLayout>
<include
layout="@layout/m_divider_h"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom" />
</FrameLayout>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:paddingStart="@dimen/def_indent"
android:paddingTop="14dp"
android:paddingEnd="@dimen/def_indent"
android:paddingBottom="14dp">
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/sw_item"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_marginEnd="2dp"
android:button="@null"
android:drawableEnd="?android:attr/listChoiceIndicatorMultiple"
android:textColor="@color/field_content"
android:textSize="@dimen/iw_text_size"
tools:text="Some text field" />
<include
layout="@layout/w_field_description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<include
layout="@layout/m_divider_h"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom" />
</FrameLayout>

View file

@ -2,9 +2,17 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share_white_18dp"
android:title="Share"
android:visible="false"
app:showAsAction="always" />
<item <item
android:id="@+id/action_favorite" android:id="@+id/action_favorite"
android:title="" android:title=""
app:actionLayout="@layout/menu_item_switch" app:actionLayout="@layout/menu_item_switch"
app:showAsAction="always" /> app:showAsAction="always" />
</menu> </menu>

View file

@ -4,6 +4,10 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@+id/nav_entry_point"> app:startDestination="@+id/nav_entry_point">
<action
android:id="@+id/action_nav_card_action_to_response_screen"
app:destination="@+id/nav_card_response" />
<fragment <fragment
android:id="@+id/nav_entry_point" android:id="@+id/nav_entry_point"
android:name="com.tangem.tangemtest._main.entryPoint.ActionListFragment" android:name="com.tangem.tangemtest._main.entryPoint.ActionListFragment"
@ -63,9 +67,9 @@
<fragment <fragment
android:id="@+id/nav_personalize" android:id="@+id/nav_personalize"
android:name="com.tangem.tangemtest.ucase.variants.personalize.ui.PersonalizeFragment" android:name="com.tangem.tangemtest.ucase.variants.personalize.ui.PersonalizationFragment"
android:label="@string/action_personalize" android:label="@string/action_personalize"
tools:layout="@layout/fg_personalize" /> tools:layout="@layout/fg_personalization" />
<fragment <fragment
android:id="@+id/nav_depersonalize" android:id="@+id/nav_depersonalize"
@ -121,4 +125,10 @@
android:label="@string/action_wallet_purge" android:label="@string/action_wallet_purge"
tools:layout="@layout/fg_action_wallet_purge" /> tools:layout="@layout/fg_action_wallet_purge" />
<fragment
android:id="@+id/nav_card_response"
android:name="com.tangem.tangemtest.ucase.variants.responses.ui.ResponseFragment"
android:label=""
tools:layout="@layout/fg_card_response" />
</navigation> </navigation>

View file

@ -8,7 +8,14 @@
<color name="edit_text_underline">#E6959595</color> <color name="edit_text_underline">#E6959595</color>
<color name="action_name">#2B2B2B</color> <color name="action_name">#2B2B2B</color>
<color name="field_description">#888888</color>
<color name="field_info">#888888</color>
<color name="field_content">#303030</color>
<color name="field_description">@color/field_info</color>
<color name="group_card_data">#D7E4F3</color>
<color name="group_signing_method">#D7F3E6</color>
<color name="switchTrack">#C6C6C6</color> <color name="switchTrack">#C6C6C6</color>
<color name="switchTrackActive">#FFE391</color> <color name="switchTrackActive">#FFE391</color>

View file

@ -1,10 +1,14 @@
<resources> <resources>
<string name="app_name">Tangem Demo</string> <string name="app_name">Tangem Development Kit</string>
<string name="fg_name_entry_point">@string/app_name</string> <string name="fg_name_entry_point">@string/app_name</string>
<string name="current_card_id">"Current card ID: "</string> <string name="fg_name_response_scan">Scan response</string>
<string name="fg_name_response_sign">Sign response</string>
<string name="fg_name_response_personalization">Personalization response</string>
<string name="fg_name_response_depersonalization">Depersonalization response</string>
<string name="unknown">unknown</string> <string name="unknown">unknown</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="switch_description">Docs</string> <string name="switch_description">Docs</string>
<string name="card_error_not_personalized">Your card hasn\'t been personalized yet. You need to run personalize command first.</string> <string name="card_error_not_personalized">Your card hasn\'t been personalized yet. You need to run personalize command first.</string>

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Field names - Response: Card -->
<string name="response_card_cid">CID</string>
<string name="response_card_manufacturer_name">Manufacturer_Name</string>
<string name="response_card_status">Status</string>
<string name="response_card_firmware_version">Firmware_Version</string>
<string name="response_card_public_key">Card_PublicKey</string>
<string name="response_card_settings_mask">Settings_Mask</string>
<string name="response_card_is_reusable">Is_Reusable</string>
<string name="response_card_use_activation">Use_Activation</string>
<string name="response_card_purge_wallet">Prohibit_Purge_Wallet</string>
<string name="response_card_use_block">Use_Block</string>
<string name="response_card_allow_pin">Allow_SET_PIN1</string>
<string name="response_card_allow_pin2">Allow_SET_PIN2</string>
<string name="response_card_use_cvc">Use_CVC</string>
<string name="response_card_default_pin">Prohibit_Default_PIN1</string>
<string name="response_card_one_apdu_at_time">Use_One_CommandAtTime</string>
<string name="response_card_use_ndef">Use_NDEF</string>
<string name="response_card_use_dynamic_ndef">Use_Dynamic_NDEF</string>
<string name="response_card_smart_security_delay">Smart_Security_Delay</string>
<string name="response_card_allow_unencrypted">Allow_Unencrypted</string>
<string name="response_card_allow_fast_encryption">Allow_Fast_Encryption</string>
<string name="response_card_protect_issuer_data_against_replay">Protect_Issuer_Data_Against_Replay</string>
<string name="response_card_allow_select_blockchain">Allow_Select_Blockchain</string>
<string name="response_card_disable_precomputed_ndef">Disable_Precomputed_NDEF</string>
<string name="response_card_security_delay_if_validated">Skip_Security_Delay_If_Validated_By_Issuer</string>
<string name="response_card_skip_pin2_cvc_if_validated_by_issuer">Skip_Check_PIN2_and_CVC_If_Validated_By_Issuer</string>
<string name="response_card_skip_security_delay_if_validated_by_linked_terminal">Skip_Security_Delay_If_Validated_By_Linked_Terminal</string>
<string name="response_card_restrict_overwrite_issuer_ex_data">Restrict_Overwrite_Issuer_Extra_Data</string>
<string name="response_card_prohibit_overwriting_issuer_ex_data">1 prohibit overwriting Issuer_Extra_Data</string>
<string name="response_card_require_terminal_tx_sig">Require_Term_Tx_Signature</string>
<string name="response_card_require_terminal_cert_sig">Require_Term_Cert_Signature</string>
<string name="response_card_check_pin3">Check_PIN3_on_Card</string>
<string name="response_card_card_data">Card_Data</string>
<string name="response_card_issuer_data_public_key">Issuer_Data_PublicKey</string>
<string name="response_card_curve">Curve_ID</string>
<string name="response_card_max_signatures">Max_Signatures</string>
<string name="response_card_signing_method">Signing_Method</string>
<string name="response_card_pause_before_pin2">Pause_Before_PIN2</string>
<string name="response_card_wallet_public_key">Wallet_PublicKey</string>
<string name="response_card_wallet_remaining_signatures">Wallet_Remaining_Signatures</string>
<string name="response_card_wallet_signed_hashes">Wallet_Signed_Hashes</string>
<string name="response_card_health">Health</string>
<string name="response_card_is_activated">Is_Activated</string>
<string name="response_card_activation_seed">Activation_Seed</string>
<string name="response_card_payment_flow_version">Payment_Flow_Version</string>
<string name="response_card_user_counter">User_Counter</string>
<string name="response_card_user_protected_counter">User_Protected_Counter</string>
<string name="info_response_card_cid">Unique Tangem card ID number</string>
<string name="info_response_card_manufacturer_name">Name of Tangem card manufacturer</string>
<string name="info_response_card_status">Current status of the card</string>
<string name="info_response_card_firmware_version">Version of Tangem COS</string>
<string name="info_response_card_public_key">Public key that is used to authenticate the card against manufacturers database. It is generated one time during card manufacturing. See Security section for more details.</string>
<string name="info_response_card_settings_mask">Card settings defined by personalization (bit mask: 0 Enabled, 1 Disabled):</string>
<string name="info_response_card_is_reusable">Defines what happens when user calls PURGE_WALLET command:
\n0 - Card will switch to Purged state
\n1 - Card will switch to Empty state and let create a new wallet again</string>
<string name="info_response_card_use_activation"></string>
<string name="info_response_card_purge_wallet">0 Card will accept PURGE_WALLET command
\n1 Card will reject PURGE_WALLET command</string>
<string name="info_response_card_use_block"></string>
<string name="info_response_card_allow_pin">Is user allowed to change PIN1 with SET_PIN command</string>
<string name="info_response_card_allow_pin2">Is user allowed to change PIN2 with SET_PIN command</string>
<string name="info_response_card_use_cvc">All commands requiring PIN2 will also require additional CVC code printed on the card</string>
<string name="info_response_card_default_pin">SET_PIN commands will not change PIN1 to default value (000000).</string>
<string name="info_response_card_one_apdu_at_time">Card will execute only one command during one communication session, thus requiring user to physically take the card away from the host after each action (all commands except for READ_CARD).</string>
<string name="info_response_card_use_ndef">Whether the card should emulate NDEF. In default configuration, two NDEF records are loaded during personalization: (1) Tangem web site address, (2) name of Android App package in Google Play Store.</string>
<string name="info_response_card_use_dynamic_ndef">0 Disable dynamic generation of NDEF for iOS. See Dynamic NDEF section for more details.
/n1 Enable dynamic NDEF for iOS.</string>
<string name="info_response_card_smart_security_delay">Security delay Pause_Before_PIN2 will not be applied if PIN2 is not default.</string>
<string name="info_response_card_allow_unencrypted">Whether the card supports unencrypted NFC communication. See NFC communication section for more details.</string>
<string name="info_response_card_allow_fast_encryption">Whether the card supports fast encrypted NFC communication. See NFC communication section for more details.</string>
<string name="info_response_card_protect_issuer_data_against_replay">0 No replay protection on write issuer data
/n1 Enable replay protection on write issuer data (card will require additional Issuer_Data_Counter incremented on each write)</string>
<string name="info_response_card_allow_select_blockchain">0 Wallet elliptic curve and blockchain information stored during PERSONALIZE command and never change
/n1 Wallet elliptic curve and blockchain information can be changed on CREATE_WALLET command</string>
<string name="info_response_card_disable_precomputed_ndef">0 Enable precomputed dynamic NDEF to work around iPhone 7+ NFC bug.
/n1 Disable precomputed dynamic NDEF. See Dynamic NDEF section for more details.</string>
<string name="info_response_card_security_delay_if_validated">0 Enforce security delay in SIGN command if the issuer validates the transaction (for signing methods 2, 3, 4 and 5).
/n1 Skip security delay in SIGN command if the issuer validates the transaction (for signing methods 2, 3, 4 and 5).</string>
<string name="info_response_card_skip_pin2_cvc_if_validated_by_issuer">0 Require and check PIN2 and CVC in SIGN command if the issuer validates the transaction (for signing method 2, 3, 4 and 5).
/n1 Skip checking PIN2 and CVC in SIGN command if the issuer validates the transaction (for signing method 2, 3, 4 and 5).</string>
<string name="info_response_card_skip_security_delay_if_validated_by_linked_terminal">1 - Store Terminal_PublicKey public key of linked terminal no each SIGN command, skip security delay if valid signature of transaction is made with Terminal_PrivateKey is provided in SIGN command</string>
<string name="info_response_card_restrict_overwrite_issuer_ex_data"></string>
<string name="info_info_response_card_prohibit_overwriting_issuer_ex_data"></string>
<string name="info_response_card_require_terminal_tx_sig">0 Skip checking terminals signature when signing POS transaction
/n1 Check terminals signature when signing POS transaction</string>
<string name="info_response_card_require_terminal_cert_sig">0 Skip checking acquirers signature of terminal certificate when signing POS transaction
/n1 Check acquirers signature of terminal certificate when signing POS transaction</string>
<string name="info_response_card_check_pin3">0 Additionally encrypt POS transaction signature with key derived from PIN3 when the transaction amount exceeds PIN3 floor limit
/n1 Require terminal to send PIN3 to card when the POS transaction amount exceeds PIN3_Floor_Limit</string>
<string name="info_response_card_card_data">Detailed information about card contents. Format is defined by the card issuer. Cards complaint with Tangem Wallet application should have TLV format described in Personalization section.</string>
<string name="info_response_card_issuer_data_public_key">Public key that is used by the card issuer to sign Issuer_Data field. See Security section for more details.</string>
<string name="info_response_card_curve">Explicit text name of the elliptic curve used for all wallet key operations.</string>
<string name="info_response_card_max_signatures">Total number of signatures allowed for the wallet when the card was personalized.</string>
<string name="info_response_card_signing_method">Defines what data should be submitted to SIGN command.</string>
<string name="info_response_card_pause_before_pin2">Delay in seconds before COS executes commands protected by PIN2.</string>
<string name="info_response_card_wallet_public_key">Public key of the blockchain wallet. Value returned only if the wallet has already been created by CREATE_WALLET command. See Security section for more details.</string>
<string name="info_response_card_wallet_remaining_signatures">Remaining number of SIGN operations before the wallet will stop signing transactions. Value returned only if the wallet has already been created by CREATE_WALLET command.</string>
<string name="info_response_card_wallet_signed_hashes">Total number of signed single hashes returned by the card in SIGN command responses since card personalization. Sums up array elements within all SIGN commands.</string>
<string name="info_response_card_health">Any non-zero value indicates that the card experiences some hardware problems. User should withdraw the value to other blockchain wallet as soon as possible. Non-zero Health tag will also appear in responses of all other commands.</string>
<string name="info_response_card_is_activated">Whether the card requires issuers confirmation of activation.
/n0 card will require issuers confirmation of activation,
/notherwise this field will not be returned (card is activated and operational).</string>
<string name="info_response_card_activation_seed">A random challenge generated by PERSONALIZE command that should be signed and returned to COS by the issuer to confirm the card has been activated. See ACTIVATE_CARD command for more details.
/nThis field will not be returned if the card is activated.</string>
<string name="info_response_card_payment_flow_version">Version of POS payment scheme supported by COS ([0x02,0x01] for version 2.30)
/nReturned only if SigningMethod 6 enabling POS transactions is supported by card.</string>
<string name="info_response_card_user_counter">This value can be initialized by App and will be increased by COS with the execution of each SIGN command. For example, this field can store blockchain “nonce” for a quick one-touch transaction on POS terminals. Returned only if SigningMethod =6.</string>
<string name="info_response_card_user_protected_counter">This value can be initialized by App (with PIN2 confirmation) and will be increased by COS with the execution of each SIGN command. For example, this field can store blockchain “nonce” for a quick one-touch transaction on POS terminals. Returned only if SigningMethod =6.</string>
<!-- Field names - Response: Card.cardData -->
<string name="response_card_card_data_batch_id">Batch_ID</string>
<string name="response_card_card_data_manufacture_date_time">Manufacture_Date_Time</string>
<string name="response_card_card_data_issuer_name">Issuer_Name</string>
<string name="response_card_card_data_blockchain_name">Blockchain_Name</string>
<string name="response_card_card_data_manufacturer_signature">Manufacturer_Signature</string>
<string name="response_card_card_data_product_mask">ProductMask</string>
<string name="response_card_card_data_token_symbol">tokenSymbol</string>
<string name="response_card_card_data_token_contract_address">tokenContractAddress</string>
<string name="response_card_card_data_token_decimal">tokenDecimal</string>
<string name="info_response_card_card_data_batch_id">Tangem internal manufacturing batch ID, should correspond with the data in Tangems card list (see Card attestation and UID and CID sections)</string>
<string name="info_response_card_card_data_manufacture_date_time">Timestamp of manufacturing should correspond with the data in Tangems card list (see Card attestation section).</string>
<string name="info_response_card_card_data_issuer_name">Name of the issuer</string>
<string name="info_response_card_card_data_blockchain_name">Name of the blockchain (once personalized, the target blockchain is fixed for the whole life cycle)</string>
<string name="info_response_card_card_data_manufacturer_signature">Signature of CID with the MANUFACTURER_PRIVATE_KEY</string>
<string name="info_response_card_card_data_product_mask">Mask of products enabled on card</string>
<string name="info_response_card_card_data_token_symbol"></string>
<string name="info_response_card_card_data_token_contract_address"></string>
<string name="info_response_card_card_data_token_decimal"></string>
</resources>