Updated on 2026-08-14
This commit is contained in:
commit
f3fc2a80d1
100 changed files with 2121 additions and 1401 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -13,6 +13,10 @@ local.properties
|
|||
.idea/caches/
|
||||
.idea/libraries/
|
||||
.idea/*.xml
|
||||
.idea/codeStyles
|
||||
|
||||
# LayoutInspector files
|
||||
.captures
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
|
|
|
|||
116
.idea/codeStyles/Project.xml
generated
116
.idea/codeStyles/Project.xml
generated
|
|
@ -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>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
|
|
@ -1,5 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tangemtest
|
||||
|
||||
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.ItemLogger
|
||||
import com.tangem.tangemtest.commons.TangemLogger
|
||||
|
|
@ -23,6 +25,10 @@ class AppTangemDemo : Application() {
|
|||
ILog.setLogger(ItemLogger())
|
||||
}
|
||||
|
||||
fun sharedPreferences(name: String = "DevKitApp", mode: Int = Context.MODE_PRIVATE): SharedPreferences {
|
||||
return getSharedPreferences(name, mode)
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var appInstance: AppTangemDemo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@ package com.tangem.tangemtest._arch.structure
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface DataHolder<D> {
|
||||
var viewModel: D
|
||||
}
|
||||
|
||||
typealias Payload = MutableMap<String, Any?>
|
||||
|
||||
interface PayloadHolder {
|
||||
|
|
@ -18,11 +14,3 @@ interface PayloadHolder {
|
|||
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()
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ package com.tangem.tangemtest._arch.structure
|
|||
*/
|
||||
interface Id
|
||||
|
||||
class StringId(val value: String) : Id
|
||||
|
||||
class StringResId(val value: Int) : Id
|
||||
|
||||
enum class Additional : Id {
|
||||
UNDEFINED,
|
||||
JSON_INCOMING,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import com.tangem.tangemtest._arch.structure.Id
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
|
||||
var foundItem: BaseItem<Any?>? = null
|
||||
fun List<Item>.findItem(id: Id): Item? {
|
||||
var foundItem: Item? = null
|
||||
iterate {
|
||||
if (it.id == id) {
|
||||
foundItem = it as? BaseItem<Any?>
|
||||
foundItem = it
|
||||
return@iterate
|
||||
}
|
||||
}
|
||||
|
|
@ -19,8 +19,8 @@ fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
|
|||
fun List<Item>.iterate(func: (Item) -> Unit) {
|
||||
forEach {
|
||||
when (it) {
|
||||
is BaseItem<*> -> func(it)
|
||||
is Block -> it.itemList.iterate(func)
|
||||
is BaseItem -> func(it)
|
||||
is ItemGroup -> it.itemList.iterate(func)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,26 @@
|
|||
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.Payload
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Item : PayloadHolder {
|
||||
interface Item {
|
||||
val id: Id
|
||||
var parent: Item?
|
||||
var viewModel: ItemViewModel
|
||||
|
||||
fun added(parent: Item) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
abstract class BaseItem<D>(
|
||||
override var viewModel: ItemViewModel<D>
|
||||
) : Item, DataHolder<ItemViewModel<D>> {
|
||||
override var parent: Item? = null
|
||||
override val payload: Payload = mutableMapOf()
|
||||
fun removed(parent: Item) {
|
||||
this.parent = null
|
||||
}
|
||||
|
||||
fun getData(): D? = viewModel.data
|
||||
fun <D> getData(): D? = viewModel.data as? D
|
||||
|
||||
fun setData(value: D?) {
|
||||
fun setData(value: Any?) {
|
||||
viewModel.data = value
|
||||
}
|
||||
|
||||
|
|
@ -29,3 +28,12 @@ abstract class BaseItem<D>(
|
|||
setData(viewModel.defaultData)
|
||||
}
|
||||
}
|
||||
|
||||
open class BaseItem(
|
||||
override val id: Id,
|
||||
override var viewModel: ItemViewModel
|
||||
) : Item {
|
||||
|
||||
override var parent: Item? = null
|
||||
|
||||
}
|
||||
|
|
@ -2,45 +2,47 @@ package com.tangem.tangemtest._arch.structure.abstraction
|
|||
|
||||
import com.tangem.tangemtest._arch.structure.ILog
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.ItemListHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Block : Item {
|
||||
interface ItemGroup : 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 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>) {
|
||||
ILog.d(this, "setItems into: $id, count: ${list.size}")
|
||||
itemList.forEach { it.removed(this) }
|
||||
itemList.clear()
|
||||
itemList.addAll(list)
|
||||
itemList.forEach { it.parent = this }
|
||||
list.forEach { addItem(it) }
|
||||
}
|
||||
|
||||
override fun getItems(): MutableList<Item> {
|
||||
return itemList
|
||||
}
|
||||
override fun getItems(): MutableList<Item> = itemList
|
||||
|
||||
override fun addItem(item: Item) {
|
||||
ILog.d(this, "addItem into: $id, who: ${item.id}")
|
||||
item.parent = this
|
||||
itemList.add(item)
|
||||
item.added(this)
|
||||
}
|
||||
|
||||
override fun removeItem(item: Item) {
|
||||
ILog.d(this, "removeItem from: $id, which: ${item.id}")
|
||||
itemList.remove(item)
|
||||
item.removed(this)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
|
|
@ -11,57 +11,12 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder
|
|||
typealias ValueChange<V> = (V?) -> Unit
|
||||
typealias SafeValueChange<V> = (V) -> Unit
|
||||
|
||||
interface ItemViewModel<D : Any?> : PayloadHolder {
|
||||
val viewState: ViewState
|
||||
var data: D?
|
||||
var defaultData: D?
|
||||
var onDataUpdated: ValueChange<D>?
|
||||
class KeyValue(val key: String, val value: Any)
|
||||
|
||||
fun updateDataByView(data: D?)
|
||||
}
|
||||
|
||||
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
|
||||
class ViewState(
|
||||
var isHidden: Boolean = false,
|
||||
var backgroundColor: Int? = -1
|
||||
) {
|
||||
|
||||
var descriptionVisibility: Int = 0x00000008
|
||||
set(value) {
|
||||
|
|
@ -71,3 +26,60 @@ class ViewState {
|
|||
|
||||
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)
|
||||
|
|
@ -1,19 +1,39 @@
|
|||
package com.tangem.tangemtest._arch.structure.impl
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.*
|
||||
|
||||
/**
|
||||
[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?)
|
||||
: BaseItem<ListValueWrapper>(ListViewModel(ListValueWrapper(selectedValue, value))
|
||||
)
|
||||
open class TypedItem<D>(id: Id, viewModel: ItemViewModel) : BaseItem(id, viewModel) {
|
||||
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))
|
||||
}
|
||||
|
|
@ -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>)
|
||||
|
|
@ -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?
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tangemtest._main
|
|||
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
|
@ -12,8 +11,9 @@ import androidx.navigation.findNavController
|
|||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.tangem.tangemtest.R
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -34,8 +34,8 @@ class MainActivity : AppCompatActivity() {
|
|||
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
|
||||
|
||||
val navController = host.navController
|
||||
appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_entry_point))
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
appBarConfiguration = AppBarConfiguration(navController.graph)
|
||||
toolbar.setupWithNavController(navController, appBarConfiguration)
|
||||
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
val dest: String = try {
|
||||
|
|
@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() {
|
|||
} catch (e: Resources.NotFoundException) {
|
||||
destination.id.toString()
|
||||
}
|
||||
Log.d("NavigationActivity", "Navigated to $dest")
|
||||
Log.d(this, "Navigated to $dest")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.tangemtest._main
|
|||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -9,7 +11,16 @@ import androidx.lifecycle.ViewModel
|
|||
class MainViewModel : ViewModel() {
|
||||
val ldDescriptionSwitch = MutableLiveData<Boolean>(false)
|
||||
|
||||
var responseEvent: TaskEvent<*>? = null
|
||||
|
||||
fun switchToggled(state: Boolean) {
|
||||
ldDescriptionSwitch.postValue(state)
|
||||
}
|
||||
|
||||
fun changeResponseEvent(event: TaskEvent<*>?) {
|
||||
Log.d(this, "changeResponseEvent")
|
||||
val reqEvent = event ?: return
|
||||
|
||||
responseEvent = reqEvent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
package com.tangem.tangemtest._main.entryPoint
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import com.tangem.tangemtest.ucase.ui.BaseFragment
|
||||
import kotlinx.android.synthetic.main.fg_entry_point.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ActionListFragment : Fragment() {
|
||||
class ActionListFragment : BaseFragment() {
|
||||
|
||||
private val navController: NavController by lazy { findNavController() }
|
||||
private lateinit var rvActions: RecyclerView
|
||||
|
||||
private val mainActivityVM: MainViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fg_entry_point, container, false)
|
||||
}
|
||||
override fun getLayoutId(): Int = R.layout.fg_entry_point
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
@ -48,7 +42,9 @@ class ActionListFragment : Fragment() {
|
|||
|
||||
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 {
|
||||
vhDataWrapper.descriptionIsVisible = it
|
||||
TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition())
|
||||
|
|
@ -64,10 +60,6 @@ class ActionListFragment : Fragment() {
|
|||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun navigate(destinationId: Int) {
|
||||
navController.navigate(destinationId, null, getDefaultNavigationOptions())
|
||||
}
|
||||
|
||||
private fun getNavigateOptions(): MutableList<ActionType> {
|
||||
return mutableListOf(
|
||||
ActionType.Scan,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.tangemtest.commons
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Store<M> {
|
||||
fun save(config: M)
|
||||
fun restore(): M
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tangemtest.commons
|
|||
/**
|
||||
[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)
|
||||
else onFail?.invoke()
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -43,4 +43,6 @@ abstract class BaseAction : Action {
|
|||
}
|
||||
callback(commandResult, modifiedItems)
|
||||
}
|
||||
|
||||
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
|
|||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
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.variants.TlvId
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ import com.tangem.tangemtest.ucase.variants.TlvId
|
|||
*/
|
||||
class DepersonalizeAction : BaseAction() {
|
||||
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
|
||||
|
||||
attrs.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
|
|||
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
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.NumberItem
|
||||
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.ItemError
|
||||
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.fromTo.PersonalizeConfigToCardConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizeParams
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigConverter
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizationParams
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
|
|
@ -33,12 +33,12 @@ class PersonalizeAction : BaseAction() {
|
|||
return
|
||||
}
|
||||
|
||||
val issuer = DefaultPersonalizeParams.issuer()
|
||||
val acquirer = DefaultPersonalizeParams.acquirer()
|
||||
val manufacturer = DefaultPersonalizeParams.manufacturer()
|
||||
val issuer = DefaultPersonalizationParams.issuer()
|
||||
val acquirer = DefaultPersonalizationParams.acquirer()
|
||||
val manufacturer = DefaultPersonalizationParams.manufacturer()
|
||||
|
||||
val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig())
|
||||
val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig)
|
||||
val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig())
|
||||
val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig)
|
||||
|
||||
attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) {
|
||||
handleResult(payload, it, null, attrs, callback)
|
||||
|
|
@ -46,18 +46,18 @@ class PersonalizeAction : BaseAction() {
|
|||
}
|
||||
|
||||
private fun checkSeries(itemList: List<Item>): Boolean {
|
||||
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val data = seriesItem.getData() ?: return false
|
||||
val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val data = seriesItem.getData() as? String ?: return false
|
||||
|
||||
seriesItem.setData(data.toUpperCase())
|
||||
return data.length == 2 || data.length == 4
|
||||
}
|
||||
|
||||
private fun checkNumber(itemList: List<Item>): Boolean {
|
||||
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val numberItem = itemList.findDataItem(CardNumber.Number) as? NumberItem ?: return false
|
||||
val seriesData = seriesItem.getData() ?: return false
|
||||
val numberData = stringOf(numberItem.getData())
|
||||
val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val numberItem = itemList.findItem(CardNumber.Number) as? NumberItem ?: return false
|
||||
val seriesData = seriesItem.getData() as? String ?: return false
|
||||
val numberData = stringOf(numberItem.getData() as? Number)
|
||||
|
||||
return if (seriesData.length == 2 && numberData.length > 13) false
|
||||
else !(seriesData.length == 4 && numberData.length > 11)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions
|
|||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
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.variants.TlvId
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
|
@ -12,9 +12,9 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
|||
*/
|
||||
class SignAction : BaseAction() {
|
||||
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 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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,12 +19,12 @@ typealias ActionCallback = (ActionResponse, AffectedList) -> Unit
|
|||
interface ItemsManager : PayloadHolder {
|
||||
|
||||
fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null)
|
||||
fun setItems(items: List<Item>)
|
||||
fun getItems(): List<Item>
|
||||
fun setItemChangeConsequences(consequence: ItemsChangeConsequence?)
|
||||
fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback)
|
||||
fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)?
|
||||
fun attachPayload(payload: Payload)
|
||||
|
||||
val consequence: ItemsChangeConsequence?
|
||||
}
|
||||
|
||||
interface PayloadKey {
|
||||
|
|
|
|||
|
|
@ -1,39 +1,59 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
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.AttrForAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.AffectedItemsCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[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() }
|
||||
|
||||
override val consequence: ItemsChangeConsequence? = null
|
||||
init {
|
||||
Log.d(this, "new instance created")
|
||||
}
|
||||
|
||||
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?) {
|
||||
if (itemList.isEmpty()) return
|
||||
val foundItem = itemList.findDataItem(id) ?: return
|
||||
val foundItem = itemList.findItem(id) ?: return
|
||||
|
||||
foundItem.setData(value)
|
||||
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) {
|
||||
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
|
||||
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)
|
||||
: AttrForAction = AttrForAction(tangemSdk, itemList, payload, consequence)
|
||||
|
||||
abstract fun createItemsList(): List<Item>
|
||||
: AttrForAction = AttrForAction(tangemSdk, itemList, payload, changeConsequence)
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.commands.CardStatus
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
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.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.CardError
|
||||
|
|
@ -18,18 +18,18 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI
|
|||
*/
|
||||
class AfterScanModifier : AfterActionModification {
|
||||
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 actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf()
|
||||
|
||||
return if (isNotPersonalized(card)) {
|
||||
actionView.showSnackbar(CardError.NotPersonalized)
|
||||
postUI { actionView.showActionFab(false) }
|
||||
postUI { actionView.enableActionFab(false) }
|
||||
listOf()
|
||||
} else {
|
||||
payload.set(PayloadKey.card, card)
|
||||
foundItem.setData(card.cardId)
|
||||
postUI { actionView.showActionFab(true) }
|
||||
postUI { actionView.enableActionFab(true) }
|
||||
listOf(foundItem)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.common.extensions.calculateSha256
|
|||
import com.tangem.common.extensions.calculateSha512
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
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.tunnel.ActionView
|
||||
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>? {
|
||||
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 card = payload.remove(PayloadKey.card) as Card?
|
||||
if (card == null) {
|
||||
hashItem.restoreDefaultData()
|
||||
postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.showActionFab(false) }
|
||||
postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.enableActionFab(false) }
|
||||
} else {
|
||||
val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s"
|
||||
val hashedData = when (card.curve) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.commands.Settings
|
|||
import com.tangem.commands.SettingsMask
|
||||
import com.tangem.commands.SigningMethod
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import java.lang.reflect.Type
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
|
@ -13,7 +14,7 @@ import java.util.*
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GsonInitializer {
|
||||
class ResponseJsonConverter {
|
||||
val gson: Gson by lazy { init() }
|
||||
|
||||
private fun init(): Gson {
|
||||
|
|
@ -27,6 +28,13 @@ class GsonInitializer {
|
|||
builder.setPrettyPrinting()
|
||||
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> {
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.tangemtest.ucase.resources
|
||||
|
||||
import com.tangem.tangemtest.AppTangemDemo
|
||||
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.StringResId
|
||||
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 ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
|
|
@ -14,9 +18,27 @@ open class ResourceHolder<T> : BaseTypedHolder<T, Resources>()
|
|||
|
||||
object MainResourceHolder : ResourceHolder<Id>() {
|
||||
init {
|
||||
PersonalizeResources().init(this)
|
||||
PersonalizationResources().init(this)
|
||||
ActionResources().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 {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.TypedHolder
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeResources() {
|
||||
class PersonalizationResources() {
|
||||
fun init(holder: ResourceHolder<Id>) {
|
||||
initBlock(holder)
|
||||
initCardNumber(holder)
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
package com.tangem.tangemtest.ucase.tunnel
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
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 SnackbarHolder {
|
||||
fun showSnackbar(message: String)
|
||||
fun showSnackbar(id: Int)
|
||||
fun showSnackbar(id: Id)
|
||||
}
|
||||
|
||||
interface ActionView : ViewScreen, SnackbarHolder {
|
||||
fun showActionFab(show: Boolean)
|
||||
fun enableActionFab(enable: Boolean)
|
||||
fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)? = null)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tangemtest.ucase.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.lifecycle.*
|
||||
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.Payload
|
||||
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.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 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]
|
||||
*/
|
||||
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 ldIsVerified = MutableLiveData<Boolean>()
|
||||
val ldResponse = MutableLiveData<String>()
|
||||
val ldReadResponse = MutableLiveData<String>()
|
||||
val ldParams = MutableLiveData(itemsManager.getItems())
|
||||
val seResponseEvent = SingleLiveEvent<TaskEvent<*>>()
|
||||
val seReadResponse = SingleLiveEvent<String>()
|
||||
val seResponse = SingleLiveEvent<String>()
|
||||
|
||||
val ldItemList = MutableLiveData(itemsManager.getItems())
|
||||
val seError: MutableLiveData<String> = SingleLiveEvent()
|
||||
val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent()
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
|
|||
this.tangemSdk = tangemSdk
|
||||
}
|
||||
|
||||
@Deprecated("Events must be send directly from the Widget")
|
||||
fun userChangedItem(id: Id, value: Any?) {
|
||||
itemChanged(id, value)
|
||||
}
|
||||
|
|
@ -53,11 +55,11 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif
|
|||
|
||||
//invokes Scan, Sign etc...
|
||||
fun invokeMainAction() {
|
||||
performAction(itemsManager, tangemSdk) { paramsManager, cardManager ->
|
||||
paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams ->
|
||||
performAction(itemsManager, tangemSdk) { paramsManager, tangemSdk ->
|
||||
paramsManager.invokeMainAction(tangemSdk) { response, listOfChangedParams ->
|
||||
notifier.handleActionResult(response, listOfChangedParams)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
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 val gson: Gson = GsonInitializer().gson
|
||||
private val gson: Gson = ResponseJsonConverter().gson
|
||||
|
||||
fun handleActionResult(response: CompletionResult<*>, list: List<Item>) {
|
||||
if (list.isNotEmpty()) notifyItemsChanged(list)
|
||||
|
|
@ -102,7 +110,10 @@ internal class Notifier(private val vm: ParamsViewModel) {
|
|||
|
||||
when (commandResponse) {
|
||||
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) {
|
||||
is Card -> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,9 +4,7 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
|
|
@ -17,134 +15,127 @@ 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.BaseItem
|
||||
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.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.kotlin.common.LayoutHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView {
|
||||
abstract class BaseCardActionFragment : BaseFragment(), ActionView {
|
||||
|
||||
protected val incomingParamsContainer: ViewGroup by lazy {
|
||||
mainView.findViewById<LinearLayout>(R.id.ll_incoming_params_container)
|
||||
}
|
||||
protected lateinit var mainView: View
|
||||
protected val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
|
||||
protected lateinit var itemContainer: ViewGroup
|
||||
protected lateinit var actionFab: FloatingActionButton
|
||||
|
||||
protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) }
|
||||
protected val widgetList = mutableListOf<ParameterWidget>()
|
||||
protected abstract val itemsManager: ItemsManager
|
||||
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())!! }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
Log.d(this, "onCreateView")
|
||||
mainView = inflater.inflate(getLayoutId(), container, false)
|
||||
return mainView
|
||||
}
|
||||
private val paramsWidgetList = mutableListOf<ParameterWidget>()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Log.d(this, "onViewCreated")
|
||||
|
||||
viewLifecycleOwner.lifecycle.addObserver(paramsVM)
|
||||
paramsVM.setCardManager(TangemSdk.init(requireActivity()))
|
||||
paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
|
||||
bindViews()
|
||||
viewLifecycleOwner.lifecycle.addObserver(actionVM)
|
||||
actionVM.setCardManager(TangemSdk.init(requireActivity()))
|
||||
actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
|
||||
|
||||
initFab()
|
||||
showActionFab(false)
|
||||
createWidgets { subscribeToViewModelChanges() }
|
||||
}
|
||||
|
||||
private fun initFab() {
|
||||
Log.d(this, "initFab")
|
||||
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
|
||||
protected open fun bindViews() {
|
||||
itemContainer = mainView.findViewById(R.id.ll_container)
|
||||
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")
|
||||
paramsVM.ldParams.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
actionVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
itemList.forEach { param ->
|
||||
val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param)
|
||||
widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) }
|
||||
widget.onActionBtnClickListener = paramsVM.getItemAction(param.id)
|
||||
widgetList.add(widget)
|
||||
val widget = ParameterWidget(inflateParamView(itemContainer), param)
|
||||
widget.onValueChanged = { id, value -> actionVM.userChangedItem(id, value) }
|
||||
widget.onActionBtnClickListener = actionVM.getItemAction(param.id)
|
||||
paramsWidgetList.add(widget)
|
||||
}
|
||||
widgetCreatedCallback()
|
||||
})
|
||||
}
|
||||
|
||||
private fun subscribeToViewModelChanges() {
|
||||
protected open fun subscribeToViewModelChanges() {
|
||||
Log.d(this, "subscribeToViewModelChanges")
|
||||
listenEvent()
|
||||
listenReadResponse()
|
||||
listenResponse()
|
||||
listenError()
|
||||
listenChangedItems()
|
||||
listenDescriptionSwitchChanges()
|
||||
}
|
||||
|
||||
protected open fun listenEvent() {
|
||||
actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer {
|
||||
mainActivityVM.changeResponseEvent(it)
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun listenReadResponse() {}
|
||||
|
||||
protected open fun listenResponse() {
|
||||
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) }
|
||||
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer {
|
||||
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
|
||||
tvResponse.text = it
|
||||
actionVM.seResponse.observe(viewLifecycleOwner, Observer {
|
||||
navigateTo(R.id.action_nav_card_action_to_response_screen)
|
||||
})
|
||||
}
|
||||
|
||||
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() {
|
||||
paramsVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
actionVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
itemList.forEach { item ->
|
||||
Log.d(this, "item changed from VM - name: ${item.id}")
|
||||
val dataItem = item as? BaseItem<Any?> ?: return@Observer
|
||||
Log.d(this, "item changed from VM - name: ${dataItem.id}, value:${dataItem.viewModel.data}")
|
||||
widgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
|
||||
Log.d(this, "item changed from VM - name: ${item.id}, value:${item.viewModel.data}")
|
||||
paramsWidgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun listenDescriptionSwitchChanges() {
|
||||
val mainActViewModel by activityViewModels<MainViewModel>()
|
||||
mainActViewModel.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { isEnabled ->
|
||||
widgetList.forEach { it.toggleDescriptionVisibility(isEnabled) }
|
||||
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
|
||||
actionVM.toggleDescriptionVisibility(it)
|
||||
})
|
||||
}
|
||||
|
||||
private fun inflateParamView(where: ViewGroup): ViewGroup {
|
||||
val inflater = LayoutInflater.from(where.context)
|
||||
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)
|
||||
where.addView(view)
|
||||
where.plusAssign(view)
|
||||
return view as ViewGroup
|
||||
}
|
||||
|
||||
override fun showActionFab(show: Boolean) {
|
||||
if (show) actionFab.show() else actionFab.hide()
|
||||
override fun enableActionFab(enable: Boolean) {
|
||||
actionFab.enable(enable)
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Id) {
|
||||
// MainResourceHolder.safeGet<>()
|
||||
when (id) {
|
||||
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
|
||||
else -> showSnackbar(requireContext().getString(R.string.unknown))
|
||||
}
|
||||
override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) {
|
||||
val resourceId = when (id) {
|
||||
CardError.NotPersonalized -> R.string.card_error_not_personalized
|
||||
else -> additionalHandler?.invoke(id) ?: UNDEFINED
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Int) {
|
||||
showSnackbar(requireContext().getString(id))
|
||||
if (resourceId != UNDEFINED) showSnackbar(resourceId)
|
||||
}
|
||||
|
||||
override fun showSnackbar(message: String) {
|
||||
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
abstract fun getAction(): ActionType
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,8 @@ import com.google.android.material.textfield.TextInputEditText
|
|||
import com.google.android.material.textfield.TextInputLayout
|
||||
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.Item
|
||||
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import com.tangem.tangemtest.ucase.resources.Resources
|
||||
|
|
@ -29,7 +29,7 @@ class ParameterWidget(
|
|||
) {
|
||||
|
||||
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 onActionBtnClickListener: (() -> Unit)? = null
|
||||
|
|
@ -47,11 +47,11 @@ class ParameterWidget(
|
|||
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
|
||||
|
||||
private var actionBtnVisibilityState: Int = btnAction.visibility
|
||||
private var value: Any? = dataItem.viewModel.data
|
||||
private var value: Any? = typedItem.viewModel.data
|
||||
|
||||
init {
|
||||
tilValue.hint = tilValue.context.getString(getResNameId())
|
||||
etValue.setText(stringOf(dataItem.viewModel.data))
|
||||
etValue.setText(stringOf(typedItem.viewModel.data))
|
||||
etValue.addTextChangedListener(valueWatcher)
|
||||
btnAction.setOnClickListener { onActionBtnClickListener?.invoke() }
|
||||
btnAction.setText(getResNameId(ActionType.Scan))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.tangem.tangemtest.ucase.variants.depersonalize.ui
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
|
|
@ -9,8 +10,7 @@ import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
|
|||
*/
|
||||
class DepersonalizeActionFragment : BaseCardActionFragment() {
|
||||
|
||||
override val itemsManager: ItemsManager by lazy { DepersonalizeItemsManager() }
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fg_depersonalize
|
||||
|
||||
override fun getAction(): ActionType = ActionType.Depersonalize
|
||||
|
||||
}
|
||||
|
|
@ -6,11 +6,9 @@ import com.tangem.tangemtest._arch.structure.Id
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
interface PersonalizeId : Id
|
||||
interface PersonalizationId : Id
|
||||
|
||||
interface BlockItem : PersonalizeId
|
||||
|
||||
enum class BlockId : PersonalizeId {
|
||||
enum class BlockId : PersonalizationId {
|
||||
CardNumber,
|
||||
Common,
|
||||
SigningMethod,
|
||||
|
|
@ -24,13 +22,13 @@ enum class BlockId : PersonalizeId {
|
|||
Pins,
|
||||
}
|
||||
|
||||
enum class CardNumber : BlockItem {
|
||||
enum class CardNumber : PersonalizationId {
|
||||
Series,
|
||||
Number,
|
||||
BatchId,
|
||||
}
|
||||
|
||||
enum class Common : BlockItem {
|
||||
enum class Common : PersonalizationId {
|
||||
Curve,
|
||||
Blockchain,
|
||||
BlockchainCustom,
|
||||
|
|
@ -38,7 +36,7 @@ enum class Common : BlockItem {
|
|||
CreateWallet,
|
||||
}
|
||||
|
||||
enum class SigningMethod : BlockItem {
|
||||
enum class SigningMethod : PersonalizationId {
|
||||
SignTx,
|
||||
SignTxRaw,
|
||||
SignValidatedTx,
|
||||
|
|
@ -48,7 +46,7 @@ enum class SigningMethod : BlockItem {
|
|||
SignExternal,
|
||||
}
|
||||
|
||||
enum class SignHashExProp : BlockItem {
|
||||
enum class SignHashExProp : PersonalizationId {
|
||||
PinLessFloorLimit,
|
||||
CryptoExKey,
|
||||
RequireTerminalCertSig,
|
||||
|
|
@ -56,26 +54,26 @@ enum class SignHashExProp : BlockItem {
|
|||
CheckPin3,
|
||||
}
|
||||
|
||||
enum class Denomination : BlockItem {
|
||||
enum class Denomination : PersonalizationId {
|
||||
WriteOnPersonalize,
|
||||
Denomination,
|
||||
}
|
||||
|
||||
enum class Token : BlockItem {
|
||||
enum class Token : PersonalizationId {
|
||||
ItsToken,
|
||||
Symbol,
|
||||
ContractAddress,
|
||||
Decimal
|
||||
}
|
||||
|
||||
enum class ProductMask : BlockItem {
|
||||
enum class ProductMask : PersonalizationId {
|
||||
Note,
|
||||
Tag,
|
||||
IdCard,
|
||||
IdIssuerCard
|
||||
}
|
||||
|
||||
enum class SettingsMask : BlockItem {
|
||||
enum class SettingsMask : PersonalizationId {
|
||||
IsReusable,
|
||||
NeedActivation,
|
||||
ForbidPurge,
|
||||
|
|
@ -94,12 +92,12 @@ enum class SettingsMask : BlockItem {
|
|||
RestrictOverwriteExtraIssuerData,
|
||||
}
|
||||
|
||||
enum class SettingsMaskProtocolEnc : BlockItem {
|
||||
enum class SettingsMaskProtocolEnc : PersonalizationId {
|
||||
AllowUnencrypted,
|
||||
AllowStaticEncryption
|
||||
}
|
||||
|
||||
enum class SettingsMaskNdef : BlockItem {
|
||||
enum class SettingsMaskNdef : PersonalizationId {
|
||||
UseNdef,
|
||||
DynamicNdef,
|
||||
DisablePrecomputedNdef,
|
||||
|
|
@ -108,7 +106,7 @@ enum class SettingsMaskNdef : BlockItem {
|
|||
Uri,
|
||||
}
|
||||
|
||||
enum class Pins : BlockItem {
|
||||
enum class Pins : PersonalizationId {
|
||||
Pin,
|
||||
Pin2,
|
||||
Pin3,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,18 +2,18 @@ package com.tangem.tangemtest.ucase.variants.personalize.converter
|
|||
|
||||
import com.tangem.commands.EllipticCurve
|
||||
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.dto.PersonalizeConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
||||
class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
||||
|
||||
// All registered association values can't be objects
|
||||
fun init(default: PersonalizeConfig) {
|
||||
fun init(default: PersonalizationConfig) {
|
||||
register(CardNumber.Series, Value(default.series))
|
||||
register(CardNumber.Number, Value(default.startNumber))
|
||||
register(CardNumber.BatchId, Value(default.batchId))
|
||||
|
|
|
|||
|
|
@ -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.*
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.commands.personalization.entities.Manufacturer
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
interface DefaultPersonalizeParams {
|
||||
interface DefaultPersonalizationParams {
|
||||
companion object {
|
||||
|
||||
fun issuer(): Issuer {
|
||||
|
|
@ -3,7 +3,7 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfig {
|
||||
class PersonalizationConfig {
|
||||
|
||||
// Card number
|
||||
var series = "BB"
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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.widget.TextView
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.TransitionManager
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseWidget
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResDescription
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class DescriptionWidget<D>(
|
||||
abstract class DescriptionWidget(
|
||||
parent: ViewGroup,
|
||||
data: BaseItem<D>
|
||||
) : BaseWidget<D>(parent, data) {
|
||||
item: Item
|
||||
) : BaseAppWidget(parent, item) {
|
||||
|
||||
private val descriptionContainer: ViewGroup by lazy { view.findViewById<ViewGroup>(R.id.container_description) }
|
||||
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
|
||||
|
||||
init {
|
||||
Log.d(this, "init id: ${dataItem.id}")
|
||||
Log.d(this, "init id: ${item.id}")
|
||||
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 descriptionId = getResDescription() ?: return
|
||||
val descriptionId = getResDescriptionId() ?: return
|
||||
val description = tv.context.getString(descriptionId)
|
||||
if (description.isEmpty()) return
|
||||
|
||||
|
|
@ -41,14 +39,3 @@ abstract class DescriptionWidget<D>(
|
|||
descriptionContainer.visibility = state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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.TextWatcher
|
||||
|
|
@ -7,14 +7,17 @@ import com.google.android.material.textfield.TextInputEditText
|
|||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.tangem.tangemtest.R
|
||||
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.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[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
|
||||
|
||||
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 {
|
||||
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) {}
|
||||
|
|
@ -30,10 +33,10 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<
|
|||
}
|
||||
|
||||
init {
|
||||
tilItem.hint = tilItem.context.getString(getResNameId())
|
||||
etItem.setText(dataItem.viewModel.data)
|
||||
tilItem.hint = getName()
|
||||
etItem.setText(typedItem.getTypedData())
|
||||
etItem.addTextChangedListener(watcher)
|
||||
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
|
||||
item.viewModel.onDataUpdated = { silentUpdate(it as? String) }
|
||||
}
|
||||
|
||||
private fun silentUpdate(value: String?) {
|
||||
|
|
@ -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.widget.TextView
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.TextItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
|
||||
/**
|
||||
[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
|
||||
|
||||
private val tvName = view.findViewById<TextView>(R.id.tv_name)
|
||||
|
||||
init {
|
||||
tvName?.setText(getResNameId())
|
||||
tvName.text = getName()
|
||||
}
|
||||
}
|
||||
|
|
@ -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.InputFilter
|
||||
|
|
@ -9,7 +9,6 @@ import com.google.android.material.textfield.TextInputLayout
|
|||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.NumberItem
|
||||
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.moveCursorToEnd
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
|
@ -17,7 +16,11 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
|||
/**
|
||||
[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
|
||||
|
||||
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 {
|
||||
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) {}
|
||||
|
|
@ -33,14 +36,14 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
|
|||
}
|
||||
|
||||
init {
|
||||
tilItem.hint = tilItem.context.getString(getResNameId())
|
||||
tilItem.hint = getName()
|
||||
|
||||
//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)
|
||||
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
|
||||
item.viewModel.onDataUpdated = { silentUpdate(it as? Number) }
|
||||
}
|
||||
|
||||
private fun silentUpdate(value: Number?) {
|
||||
|
|
@ -53,5 +56,4 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
|
|||
private fun getValue(value: String): Long {
|
||||
return if (value.isEmpty()) 0L else value.toLong()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.ViewGroup
|
||||
|
|
@ -7,50 +7,52 @@ import android.widget.BaseAdapter
|
|||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.KeyValue
|
||||
import com.tangem.tangemtest._arch.structure.impl.ListItem
|
||||
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.KeyValue
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ListViewModel
|
||||
import com.tangem.tangemtest._arch.structure.impl.SpinnerItem
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[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
|
||||
|
||||
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 spAdapter = SpItemAdapter(data.itemList)
|
||||
private val spinner = view.findViewById<Spinner>(R.id.sp_item)
|
||||
private val spAdapter = SpItemAdapter(viewModel.itemList)
|
||||
|
||||
private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
data.selectedItem = data.itemList[position]
|
||||
dataItem.viewModel.updateDataByView(data)
|
||||
viewModel.selectedItem = viewModel.itemList[position].value
|
||||
item.viewModel.updateDataByView(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val name = view.findViewById<TextView>(R.id.tv_name)
|
||||
name.setText(getResNameId())
|
||||
spItem.adapter = spAdapter
|
||||
spAdapter.getItemPosition(stringOf(data.selectedItem))?.let {
|
||||
spItem.setSelection(it)
|
||||
name.text = getName()
|
||||
spinner.adapter = spAdapter
|
||||
spAdapter.getItemPosition(stringOf(viewModel.selectedItem))?.let {
|
||||
spinner.setSelection(it)
|
||||
}
|
||||
spItem.onItemSelectedListener = onItemSelectedListener
|
||||
dataItem.viewModel.onDataUpdated = {
|
||||
it?.apply {
|
||||
spItem.onItemSelectedListener = null
|
||||
this.itemList.firstOrNull { item -> item.value == selectedItem }?.let {
|
||||
val position = itemList.indexOf(it)
|
||||
spItem.setSelection(position)
|
||||
}
|
||||
spItem.onItemSelectedListener = onItemSelectedListener
|
||||
spinner.onItemSelectedListener = onItemSelectedListener
|
||||
item.viewModel.onDataUpdated = {
|
||||
val selectedItem = it as? String
|
||||
spinner.onItemSelectedListener = null
|
||||
viewModel.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue ->
|
||||
val position = viewModel.itemList.indexOf(keyValue)
|
||||
spinner.setSelection(position)
|
||||
}
|
||||
spinner.onItemSelectedListener = onItemSelectedListener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.widget.CompoundButton
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.BoolItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
|
||||
/**
|
||||
[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
|
||||
|
||||
private val switchItem = view.findViewById<SwitchCompat>(R.id.sw_item)
|
||||
|
||||
private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
|
||||
dataItem.viewModel.updateDataByView(isChecked)
|
||||
typedItem.viewModel.updateDataByView(isChecked)
|
||||
}
|
||||
|
||||
init {
|
||||
switchItem.setText(getResNameId())
|
||||
switchItem.isChecked = dataItem.viewModel.data ?: false
|
||||
switchItem.text = getName()
|
||||
switchItem.isChecked = typedItem.getTypedData() ?: false
|
||||
switchItem.setOnCheckedChangeListener(changeListener)
|
||||
dataItem.viewModel.onDataUpdated = {
|
||||
typedItem.viewModel.onDataUpdated = {
|
||||
switchItem.setOnCheckedChangeListener(null)
|
||||
switchItem.isChecked = it ?: false
|
||||
switchItem.isChecked = it as? Boolean ?: false
|
||||
switchItem.setOnCheckedChangeListener(changeListener)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,36 +2,29 @@ package com.tangem.tangemtest.ucase.variants.scan.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.Observer
|
||||
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 ru.dev.gbixahue.eu4d.lib.android._android.views.show
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
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?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
showActionFab(true)
|
||||
enableActionFab(true)
|
||||
}
|
||||
|
||||
override fun listenResponse() {
|
||||
val tvStub = mainView.findViewById<TextView>(R.id.tv_screen_stub)
|
||||
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) }
|
||||
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
|
||||
override fun listenReadResponse() {
|
||||
actionVM.seReadResponse.observe(viewLifecycleOwner, Observer {
|
||||
navigateTo(R.id.action_nav_card_action_to_response_screen)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package com.tangem.tangemtest.ucase.variants.sign.ui
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
|
|
@ -9,7 +10,7 @@ import com.tangem.tangemtest.ucase.ui.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
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_incoming_params_container"
|
||||
android:id="@+id/ll_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_incoming_params_container"
|
||||
android:id="@+id/ll_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
|
|
|||
22
tangem-demo/src/main/res/layout/fg_card_response.xml
Normal file
22
tangem-demo/src/main/res/layout/fg_card_response.xml
Normal 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>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_incoming_params_container"
|
||||
android:id="@+id/ll_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
|
|
|||
45
tangem-demo/src/main/res/layout/w_response_item.xml
Normal file
45
tangem-demo/src/main/res/layout/w_response_item.xml
Normal 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>
|
||||
41
tangem-demo/src/main/res/layout/w_response_item_checkbox.xml
Normal file
41
tangem-demo/src/main/res/layout/w_response_item_checkbox.xml
Normal 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>
|
||||
|
|
@ -2,9 +2,17 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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
|
||||
android:id="@+id/action_favorite"
|
||||
android:title=""
|
||||
app:actionLayout="@layout/menu_item_switch"
|
||||
app:showAsAction="always" />
|
||||
|
||||
</menu>
|
||||
|
|
@ -4,6 +4,10 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
app:startDestination="@+id/nav_entry_point">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_nav_card_action_to_response_screen"
|
||||
app:destination="@+id/nav_card_response" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_entry_point"
|
||||
android:name="com.tangem.tangemtest._main.entryPoint.ActionListFragment"
|
||||
|
|
@ -63,9 +67,9 @@
|
|||
|
||||
<fragment
|
||||
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"
|
||||
tools:layout="@layout/fg_personalize" />
|
||||
tools:layout="@layout/fg_personalization" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_depersonalize"
|
||||
|
|
@ -121,4 +125,10 @@
|
|||
android:label="@string/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>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@
|
|||
<color name="edit_text_underline">#E6959595</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="switchTrackActive">#FFE391</color>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
<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="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="copy_to_clipboard">Copy to clipboard</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>
|
||||
|
|
|
|||
138
tangem-demo/src/main/res/values/strings_response.xml
Normal file
138
tangem-demo/src/main/res/values/strings_response.xml
Normal 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 manufacturer’s 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 terminal’s signature when signing POS transaction
|
||||
/n1 – Check terminal’s signature when signing POS transaction</string>
|
||||
<string name="info_response_card_require_terminal_cert_sig">0 – Skip checking acquirer’s signature of terminal certificate when signing POS transaction
|
||||
/n1 – Check acquirer’s 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 issuer’s confirmation of activation.
|
||||
/n0 – card will require issuer’s 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 Tangem’s 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 Tangem’s 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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue