Updated on 2026-08-14

This commit is contained in:
Tangem 2020-03-23 22:55:21 +03:00
parent 1d71266378
commit 8a6f9f3ff6
48 changed files with 1752 additions and 503 deletions

View file

@ -51,5 +51,5 @@ dependencies {
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.github.gbIxaHue:eu4d:0.3.4'
implementation 'com.github.gbIxaHue:eu4d:0.3.6'
}

View file

@ -0,0 +1,55 @@
{
"releaseVersion": false,
"issuerName": "TANGEM SDK",
"series": "DB",
"startNumber": 300000000,
"count": 20,
"numberFormat": "",
"PIN": "000000",
"PIN2": "000",
"PIN3": "",
"hexCrExKey": "",
"CVC": "000",
"pauseBeforePIN2": 15000,
"smartSecurityDelay": true,
"curveID": "ed25519",
"SigningMethod": 0,
"MaxSignatures": 1000000,
"isReusable": true,
"allowSwapPIN": false,
"allowSwapPIN2": true,
"useActivation": false,
"useCVC": false,
"useNDEF": true,
"useDynamicNDEF": true,
"useOneCommandAtTime": false,
"useBlock": false,
"allowSelectBlockchain": false,
"forbidPurgeWallet": false,
"protocolAllowUnencrypted": true,
"protocolAllowStaticEncryption": true,
"protectIssuerDataAgainstReplay": true,
"forbidDefaultPIN": false,
"disablePrecomputedNDEF": false,
"skipSecurityDelayIfValidatedByIssuer": false,
"skipCheckPIN2andCVCIfValidatedByIssuer": false,
"skipSecurityDelayIfValidatedByLinkedTerminal": true,
"restrictOverwriteIssuerDataEx": false,
"requireTerminalTxSignature": false,
"requireTerminalCertSignature": false,
"checkPIN3onCard": false,
"ndef": {
"type": "AAR",
"value": "com.tangem.wallet"
},
"cardData": {
"date": "2020-02-17",
"batch": "FF87",
"blockchain": "IROHA",
"product_note": true,
"product_tag": false,
"product_id_card": false
},
"createWallet": 1,
"issuerData": null
}

View file

@ -1,6 +1,8 @@
package com.tangem.tangemtest
import android.app.Application
import com.tangem.tangemtest._arch.structure.base.ULog
import com.tangem.tangemtest._arch.structure.base.UnitLogger
import com.tangem.tangemtest.commons.TangemLogger
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
@ -12,10 +14,11 @@ class AppTangemDemo : Application() {
override fun onCreate() {
super.onCreate()
setLogger()
setupLoggers()
}
private fun setLogger() {
private fun setupLoggers() {
Log.setLogger(TangemLogger())
ULog.setLogger(UnitLogger())
}
}

View file

@ -19,162 +19,162 @@ import java.nio.charset.StandardCharsets
/**
[REDACTED_AUTHOR]
*/
class TestUserDataActivity: AppCompatActivity() {
class TestUserDataActivity : AppCompatActivity() {
private lateinit var cardManager: CardManager
private lateinit var writeOptions: WriteOptions
private lateinit var cardManager: CardManager
private lateinit var writeOptions: WriteOptions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_user_data)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_user_data)
init()
initWriteOptions()
}
private fun init() {
cardManager = CardManager.init(this)
btn_scan?.setOnClickListener { _ ->
cardManager.scanCard { taskEvent ->
when (taskEvent) {
is TaskEvent.Event -> {
when (taskEvent.data) {
is ScanEvent.OnReadEvent -> {
// Handle returned card data
writeOptions.cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
runOnUiThread { showReadWriteSection(true) }
}
is ScanEvent.OnVerifyEvent -> {
//Handle card verification
}
}
}
is TaskEvent.Completion -> {
if (taskEvent.error != null) {
if (taskEvent.error is TaskError.UserCancelled) {
// Handle case when user cancelled manually
}
// Handle other errors
}
// Handle completion
}
}
}
init()
initWriteOptions()
}
btn_write.setOnClickListener {
if (writeOptions.cardId == null) return@setOnClickListener
private fun init() {
cardManager = CardManager.init(this)
cardManager.writeUserData(
writeOptions.cardId !!,
writeOptions.userData,
writeOptions.userProtectedData,
writeOptions.userCounter,
writeOptions.userProtectedCounter
) {
when (it) {
is TaskEvent.Completion -> handleError(tv_write_result, it.error)
is TaskEvent.Event -> {
runOnUiThread {
val data = it.data as? WriteUserDataResponse
if (data == null) {
tv_write_result.text = "Response doesn't match"
return@runOnUiThread
}
tv_write_result?.text = "Success"
btn_scan?.setOnClickListener { _ ->
cardManager.scanCard { taskEvent ->
when (taskEvent) {
is TaskEvent.Event -> {
when (taskEvent.data) {
is ScanEvent.OnReadEvent -> {
// Handle returned card data
writeOptions.cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
runOnUiThread { showReadWriteSection(true) }
}
is ScanEvent.OnVerifyEvent -> {
//Handle card verification
}
}
}
is TaskEvent.Completion -> {
if (taskEvent.error != null) {
if (taskEvent.error is TaskError.UserCancelled) {
// Handle case when user cancelled manually
}
// Handle other errors
}
// Handle completion
}
}
}
}
btn_write.setOnClickListener {
if (writeOptions.cardId == null) return@setOnClickListener
cardManager.writeUserData(
writeOptions.cardId!!,
writeOptions.userData,
writeOptions.userProtectedData,
writeOptions.userCounter,
writeOptions.userProtectedCounter
) {
when (it) {
is TaskEvent.Completion -> handleError(tv_write_result, it.error)
is TaskEvent.Event -> {
runOnUiThread {
val data = it.data as? WriteUserDataResponse
if (data == null) {
tv_write_result.text = "Response doesn't match"
return@runOnUiThread
}
tv_write_result?.text = "Success"
}
}
}
}
}
btn_read.setOnClickListener {
if (writeOptions.cardId == null) return@setOnClickListener
cardManager.readUserData(writeOptions.cardId!!) {
when (it) {
is TaskEvent.Completion -> handleError(tv_read_result, it.error)
is TaskEvent.Event -> {
runOnUiThread {
val data = it.data as? ReadUserDataResponse
if (data == null) {
tv_read_result.text = "Response doesn't match"
return@runOnUiThread
}
tv_read_result?.text = "Success"
writeOptions.userData = data.userData
writeOptions.userProtectedData = data.userProtectedData
writeOptions.userCounter = data.userCounter
writeOptions.userProtectedCounter = data.userProtectedCounter
tv_card_cid.text = data.cardId
tv_data.text = String(data.userData, StandardCharsets.US_ASCII)
tv_protected_data.text = String(data.userProtectedData, StandardCharsets.US_ASCII)
tv_counter.text = data.userCounter.toString()
tv_protected_counter.text = data.userProtectedCounter.toString()
}
}
}
}
}
}
}
}
btn_read.setOnClickListener {
if (writeOptions.cardId == null) return@setOnClickListener
private fun handleError(tv: TextView, error: TaskError?) {
val er = error ?: return
if (er is TaskError.UserCancelled) return
cardManager.readUserData(writeOptions.cardId !!) {
when (it) {
is TaskEvent.Completion -> handleError(tv_read_result, it.error)
is TaskEvent.Event -> {
runOnUiThread {
val data = it.data as? ReadUserDataResponse
if (data == null) {
tv_read_result.text = "Response doesn't match"
return@runOnUiThread
}
tv_read_result?.text = "Success"
writeOptions.userData = data.userData
writeOptions.userProtectedData = data.userProtectedData
writeOptions.userCounter = data.userCounter
writeOptions.userProtectedCounter = data.userProtectedCounter
tv_card_cid.text = data.cardId
tv_data.text = String(data.userData, StandardCharsets.US_ASCII)
tv_protected_data.text = String(data.userProtectedData, StandardCharsets.US_ASCII)
tv_counter.text = data.userCounter.toString()
tv_protected_counter.text = data.userProtectedCounter.toString()
}
}
}
}
runOnUiThread { tv.text = er::class.simpleName }
}
}
private fun handleError(tv: TextView, error: TaskError?) {
val er = error ?: return
if (er is TaskError.UserCancelled) return
private fun initWriteOptions() {
writeOptions = WriteOptions()
runOnUiThread { tv.text = er::class.simpleName }
}
chb_with_ud.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateData(buttonView) }
chb_with_ud_protected.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedData(buttonView) }
chb_with_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateCounter(buttonView) }
chb_with_protected_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedCounter(buttonView) }
chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) }
}
private fun initWriteOptions() {
writeOptions = WriteOptions()
chb_with_ud.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateData(buttonView) }
chb_with_ud_protected.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedData(buttonView) }
chb_with_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateCounter(buttonView) }
chb_with_protected_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedCounter(buttonView) }
chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) }
}
private fun showReadWriteSection(show: Boolean) {
val state = if (show) View.VISIBLE else View.GONE
cl_read_write.visibility = state
}
private fun showReadWriteSection(show: Boolean) {
val state = if (show) View.VISIBLE else View.GONE
cl_read_write.visibility = state
}
}
class WriteOptions {
var cardId: String? = null
var userData: ByteArray? = null
var userProtectedData: ByteArray? = null
var userCounter: Int? = null
var userProtectedCounter: Int? = null
var pin2: String? = null
var cardId: String? = null
var userData: ByteArray? = null
var userProtectedData: ByteArray? = null
var userCounter: Int? = null
var userProtectedCounter: Int? = null
var pin2: String? = null
fun updateData(chbx: CompoundButton) {
val value = "simple user data".toByteArray()
userData = if (chbx.isChecked) value else null
}
fun updateData(chbx: CompoundButton) {
val value = "simple user data".toByteArray()
userData = if (chbx.isChecked) value else null
}
fun updateProtectedData(chbx: CompoundButton) {
val value = "protected user data".toByteArray()
userProtectedData = if (chbx.isChecked) value else null
}
fun updateProtectedData(chbx: CompoundButton) {
val value = "protected user data".toByteArray()
userProtectedData = if (chbx.isChecked) value else null
}
fun updateCounter(chbx: CompoundButton) {
val value = if (userCounter == null) 0 else userCounter !! + 1
userCounter = if (chbx.isChecked) value else null
}
fun updateCounter(chbx: CompoundButton) {
val value = if (userCounter == null) 0 else userCounter!! + 1
userCounter = if (chbx.isChecked) value else null
}
fun updateProtectedCounter(chbx: CompoundButton) {
val value = if (userProtectedCounter == null) 0 else userProtectedCounter !! + 1
userProtectedCounter = if (chbx.isChecked) value else null
}
fun updateProtectedCounter(chbx: CompoundButton) {
val value = if (userProtectedCounter == null) 0 else userProtectedCounter!! + 1
userProtectedCounter = if (chbx.isChecked) value else null
}
fun updatePin2(chbx: CompoundButton) {
val value = CardEnvironment.DEFAULT_PIN2
pin2 = if (chbx.isChecked) value else null
}
fun updatePin2(chbx: CompoundButton) {
val value = CardEnvironment.DEFAULT_PIN2
pin2 = if (chbx.isChecked) value else null
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.tangemtest._arch.structure.base
/**
[REDACTED_AUTHOR]
*/
interface DataHolder<D> {
var viewModel: D?
}
interface Payload {
val payload: MutableMap<String, Any?>
}
interface ItemListHolder<I> {
fun setItems(list: MutableList<I>)
fun getItems(): MutableList<I>
fun addItem(item: I)
fun removeItem(item: I)
fun clear()
}

View file

@ -0,0 +1,36 @@
package com.tangem.tangemtest._arch.structure.base
import ru.dev.gbixahue.eu4d.lib.android.global.log.Logger
import ru.dev.gbixahue.eu4d.lib.android.global.log.TagLogger
import ru.dev.gbixahue.eu4d.lib.android.global.log.profiling.SimpleLogProfiler
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
object ULog {
private var logger: Logger? = null
fun setLogger(logger: Logger) {
ULog.logger = logger
}
fun d(from: Any, msg: Any?, value: Any? = null) {
logger?.d(from, stringOf(msg), value)
}
fun w(from: Any, msg: Any?, value: Any? = null) {
logger?.w(from, stringOf(msg), value)
}
fun e(from: Any, msg: Any?, value: Any? = null) {
logger?.e(from, stringOf(msg), value)
}
}
class UnitLogger : TagLogger("UNIT") {
init {
msProfiler = SimpleLogProfiler()
}
}

View file

@ -0,0 +1,17 @@
package com.tangem.tangemtest._arch.structure.base
/**
[REDACTED_AUTHOR]
*/
interface Unit : Payload {
var parent: Unit?
}
abstract class BaseUnit : Unit {
override var parent: Unit? = null
override val payload: MutableMap<String, Any?> = mutableMapOf()
}
open class DataUnit<D>(
override var viewModel: UnitViewModel<D>?
) : BaseUnit(), DataHolder<UnitViewModel<D>>

View file

@ -0,0 +1,53 @@
package com.tangem.tangemtest._arch.structure.base
/**
[REDACTED_AUTHOR]
*/
typealias BlockModified = () -> Unit
interface Block : Unit {
val unitList: MutableList<Unit>
var blockModified: BlockModified?
}
abstract class BaseBlock : Block {
override var parent: Unit? = null
override val unitList: MutableList<Unit> = mutableListOf()
override val payload: MutableMap<String, Any?> = mutableMapOf()
override var blockModified: BlockModified? = null
}
open class ListUnitBlock : BaseBlock(), ItemListHolder<Unit> {
override fun setItems(list: MutableList<Unit>) {
ULog.d(this, "setUnits: ${list.size}")
unitList.clear()
unitList.addAll(list)
unitList.forEach { it.parent = this }
blockModified?.invoke()
}
override fun getItems(): MutableList<Unit> {
ULog.d(this, "getUnits: ${unitList.size}")
return unitList
}
override fun addItem(item: Unit) {
ULog.d(this, "addUnit: ${unitList.size}")
item.parent = this
unitList.add(item)
blockModified?.invoke()
}
override fun removeItem(item: Unit) {
ULog.d(this, "removeUnit $item")
unitList.remove(item)
blockModified?.invoke()
}
override fun clear() {
ULog.d(this, "clear")
unitList.clear()
blockModified?.invoke()
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.tangemtest._arch.structure.base
/**
[REDACTED_AUTHOR]
*/
data class ViewState(
var isEnabled: Boolean = true,
var visibility: Int = 0x00000000,
var descriptionVisibility: Int = 0x00000008
)
interface UnitViewModel<D> : Payload {
var viewState: ViewState?
var data: D?
fun updateData(data: D?)
}
open class BaseUnitViewModel<D>(
override var data: D? = null
) : UnitViewModel<D> {
override var viewState: ViewState? = ViewState()
override val payload: MutableMap<String, Any?> = mutableMapOf()
override fun updateData(data: D?) {
ULog.d(this, "data changed: $data")
this.data = data
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.tangemtest._arch.structure.base
/**
[REDACTED_AUTHOR]
*/
typealias PluginCallBackResult = (Any?) -> Unit
interface Plugin : Unit {
fun invoke(data: Any?, asyncCallback: PluginCallBackResult? = null): Any?
}

View file

@ -0,0 +1,8 @@
package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.base.ListUnitBlock
/**
[REDACTED_AUTHOR]
*/
class LinearBlock : ListUnitBlock()

View file

@ -0,0 +1,18 @@
package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.base.DataUnit
class TextUnit(value: String? = null) : DataUnit<String>(StringViewModel(value))
class EditTextUnit(value: String? = null) : DataUnit<String>(StringViewModel(value))
class NumberUnit(value: Number? = null) : DataUnit<Number>(NumberViewModel(value))
class BoolUnit(value: Boolean? = null) : DataUnit<Boolean>(BoolViewModel(value))
class ListUnit(value: List<KeyValue>, selectedValue: Any)
: DataUnit<ModelHelper>(ListViewModel(ModelHelper(selectedValue, value))
)
fun DataUnit<*>.resName(resId: Int): DataUnit<*> {
payload["resName"] = resId
return this
}

View file

@ -0,0 +1,14 @@
package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.base.BaseUnitViewModel
/**
[REDACTED_AUTHOR]
*/
class StringViewModel(value: String?) : BaseUnitViewModel<String>(value)
class NumberViewModel(value: Number?) : BaseUnitViewModel<Number>(value)
class BoolViewModel(value: Boolean?) : BaseUnitViewModel<Boolean>(value)
class ListViewModel(value: ModelHelper?) : BaseUnitViewModel<ModelHelper>(value)
class KeyValue(val key: String, val value: Any)
class ModelHelper(var selectedItem: Any, val itemList: List<KeyValue>)

View file

@ -10,11 +10,13 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.tangem.tangemtest.R
import com.tangem.tangemtest.commons.ActionType
import com.tangem.tangemtest.commons.NavigateOptions
import com.tangem.tangemtest.commons.getDefaultNavigationOptions
import kotlinx.android.synthetic.main.fg_entry_point.*
import ru.dev.gbixahue.eu4d.lib.android._android.views.find
/**
[REDACTED_AUTHOR]
@ -31,6 +33,13 @@ class EntryPointFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
initPersonalizeNagivation()
}
private fun initPersonalizeNagivation() {
view?.find<ExtendedFloatingActionButton>(R.id.fab_personalize)?.setOnClickListener {
navigate(R.id.action_nav_entry_point_to_nav_personalize)
}
}
private fun initRecyclerView() {

View file

@ -1,31 +0,0 @@
package com.tangem.tangemtest.card_use_cases.domain
import androidx.fragment.app.FragmentActivity
import com.tangem.CardManager
import com.tangem.commands.Card
import com.tangem.tangem_sdk_new.extensions.init
/**
[REDACTED_AUTHOR]
*/
class CardContext {
lateinit var cardManager: CardManager
private set
var card: Card? = null
var isVerified: Boolean = false
fun reset() {
isVerified = false
card = null
}
fun requireCard(): Card = card!!
fun init(activity: FragmentActivity): CardContext {
if (::cardManager.isInitialized) return this
cardManager = CardManager.init(activity)
return this
}
}

View file

@ -70,4 +70,10 @@ class SignAction : BaseCardAction() {
private fun createHashes(value: String): Array<ByteArray> {
return arrayOf(value.toByteArray())
}
}
class PersonalizeAction : BaseCardAction() {
override fun executeMainAction(attrs: AttrForAction, callback: ActionCallback) {
TODO("Not yet implemented")
}
}

View file

@ -13,6 +13,7 @@ class ParamsManagerFactory : BaseTypedHolder<ActionType, ParamsManager>() {
return ParamsManagerFactory().apply {
register(ActionType.Scan, ScanParamsManager())
register(ActionType.Sign, SignParamsManager())
register(ActionType.Personalize, PersonalizeParamsManager())
}
}
}

View file

@ -2,10 +2,7 @@ package com.tangem.tangemtest.card_use_cases.domain.params_manager
import com.tangem.CardManager
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.domain.card_action.AttrForAction
import com.tangem.tangemtest.card_use_cases.domain.card_action.CardAction
import com.tangem.tangemtest.card_use_cases.domain.card_action.ScanAction
import com.tangem.tangemtest.card_use_cases.domain.card_action.SignAction
import com.tangem.tangemtest.card_use_cases.domain.card_action.*
import com.tangem.tangemtest.card_use_cases.domain.params_manager.modifiers.ParamsChangeConsequence
import com.tangem.tasks.TaskEvent
@ -51,6 +48,8 @@ abstract class BaseParamsManager(protected val action: CardAction) : ParamsManag
override fun getParams(): MutableList<IncomingParameter> = paramsList.toMutableList()
override fun getActionByTag(tag: TlvTag, cardManager: CardManager): ((ActionCallback) -> Unit)? = null
// Use it if current parameter needs to be affect any other parameters
protected open fun applyChangesByAffectedParams(param: IncomingParameter, callback: AffectedParamsCallback?) {
getChangeConsequence()?.affectChanges(param, paramsList)?.let { callback?.invoke(it) }
@ -70,8 +69,6 @@ class ScanParamsManager : BaseParamsManager(ScanAction()) {
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
action.executeMainAction(getAttrsForAction(cardManager), callback)
}
override fun getActionByTag(tag: TlvTag, cardManager: CardManager): ((ActionCallback) -> Unit)? = null
}
class SignParamsManager : BaseParamsManager(SignAction()) {
@ -89,4 +86,14 @@ class SignParamsManager : BaseParamsManager(SignAction()) {
override fun getActionByTag(tag: TlvTag, cardManager: CardManager): ((ActionCallback) -> Unit)? {
return action.getActionByTag(tag, getAttrsForAction(cardManager))
}
}
class PersonalizeParamsManager : BaseParamsManager(PersonalizeAction()) {
override fun createParamsList(): List<IncomingParameter> {
return listOf()
}
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
}
}

View file

@ -0,0 +1,70 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.ui.personalize.view_model.PersonalizeViewModel
import com.tangem.tangemtest.card_use_cases.ui.personalize.view_model.PersonalizeViewModelFactory
import com.tangem.tangemtest.card_use_cases.ui.personalize.widgets.WidgetBuilder
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
import java.io.IOException
import java.io.InputStream
import java.nio.charset.Charset
/**
[REDACTED_AUTHOR]
*/
class PersonalizeFragment : Fragment() {
protected val blockContainer: ViewGroup by lazy {
mainView.findViewById<FrameLayout>(R.id.ll_container)
}
protected val viewModel: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(getPersonalizeJson()) }
protected lateinit var mainView: View
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)
viewModel.ldBlockList.observe(viewLifecycleOwner, Observer {
WidgetBuilder().build(it, blockContainer)
})
}
private fun getPersonalizeJson(): String {
return getJsonFromAssets(requireContext(), "personalize_default.json") ?: "[]"
}
private fun getJsonFromAssets(context: Context, fileName: String): String? {
return try {
val stream: InputStream = context.assets.open(fileName)
val size: Int = stream.available()
val buffer = ByteArray(size)
stream.read(buffer)
stream.close()
String(buffer, Charset.forName("UTF-8"))
} catch (e: IOException) {
e.printStackTrace()
null
}
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test
import com.tangem.tangemtest._arch.structure.base.Block
/**
[REDACTED_AUTHOR]
*/
interface EncodeDecode<A, B> {
fun decode(from: A): B
fun encode(from: B): A
}
class JsonBlockEnDe(
private val jsonToBlock: JsonToBlockConverter,
private val blockToJson: BlockToJsonConverter
) : EncodeDecode<TestJsonDto, Block> {
override fun encode(from: Block): TestJsonDto = blockToJson.convert(from)
override fun decode(from: TestJsonDto): Block = jsonToBlock.convert(from)
}

View file

@ -0,0 +1,111 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test
class TestJsonDto {
// Card number
val series = "DB"
val startNumber = 300000000000
// Common
val curveID = "ed25519"
val blockchain = Blockchain() // новое поле
val MaxSignatures = 1000000
val createWallet = 1
val createWalletB = createWallet == 1
// Signing method
val SigningMethod0 = false
val SigningMethod1 = false
val SigningMethod2 = false
val SigningMethod3 = false
val SigningMethod4 = false
val SigningMethod5 = false
val SigningMethod6 = false
// Sign hash external properties
val pinLessFloorLimit = 0 // новое поле
val hexCrExKey = ""
val requireTerminalTxSignature = false
val requireTerminalCertSignature = false
val checkPIN3onCard = false
// Denomination
val writeOnPersonalization = false // новое поле
val denomination = 0 // новое поле
// Token
val itsToken = true // новое поле
val symbol = "token symbol" // новое поле
val contractAddress = "contact adress" // новое поле
val decimal = 1 // новое поле
val cardData = CardData()
// Settings mask
val isReusable = true
val useActivation = false
val forbidPurgeWallet = false
val allowSelectBlockchain = false
val useBlock = false
val useOneCommandAtTime = false
val useCVC = false
val allowSwapPIN = false
val allowSwapPIN2 = true
val forbidDefaultPIN = false
val smartSecurityDelay = true
val protectIssuerDataAgainstReplay = true
val skipSecurityDelayIfValidatedByIssuer = false
val skipCheckPIN2andCVCIfValidatedByIssuer = false
val skipSecurityDelayIfValidatedByLinkedTerminal = true
val restrictOverwriteIssuerDataEx = false
// Settings mask - protocol encryption
val protocolAllowUnencrypted = true
val protocolAllowStaticEncryption = true
val useNDEF = true
val useDynamicNDEF = true
val disablePrecomputedNDEF = false
val NDEF: MutableList<Ndef> = mutableListOf(Ndef("AAR", "com.tangem.wallet")) // aar, customAAR package
// Pins
val PIN = "000000"
val PIN2 = "000"
val PIN3 = ""
val CVC = "000"
val pauseBeforePIN2 = 15000
// not used
val count = 20
val numberFormat = ""
val issuerData = null
val releaseVersion = false
val issuerName = "TANGEM SDK"
}
class CardData {
val date = "2020-02-17"
val batch = "FF87"
val blockchain = "IROHA"
val product_note = true
val product_tag = false
val product_id_card = false
}
class Blockchain {
val customName: String? = null
val name = "BTC"
}
class Ndef(val type: String, val value: String)

View file

@ -0,0 +1,213 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.base.Block
import com.tangem.tangemtest._arch.structure.impl.*
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
class JsonToBlockConverter : Converter<TestJsonDto, Block> {
override fun convert(from: TestJsonDto): Block {
val initialBlock = LinearBlock()
initialBlock.addItem(cardNumber(from))
initialBlock.addItem(common(from))
initialBlock.addItem(signingMethod(from))
initialBlock.addItem(signHashExProperties(from))
initialBlock.addItem(denomination(from))
initialBlock.addItem(token(from))
initialBlock.addItem(productMask(from))
initialBlock.addItem(settingsMask(from))
initialBlock.addItem(settingsMaskProtocolEnc(from))
initialBlock.addItem(cardNumber(from))
initialBlock.addItem(settingsMaskNdef(from))
initialBlock.addItem(pins(from))
addPayload(initialBlock, from)
return initialBlock
}
private fun cardNumber(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_card_number))
block.addItem(EditTextUnit(from.series).resName(R.string.pers_item_series))
block.addItem(NumberUnit(from.startNumber).resName(R.string.pers_item_number))
return block
}
private fun common(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_common))
block.addItem(ListUnit(Helper.listOfCurves(), from.curveID).resName(R.string.pers_item_curve))
block.addItem(ListUnit(Helper.listOfBlockchain(), from.blockchain.name).resName(R.string.pers_item_blockchain))
block.addItem(EditTextUnit(from.blockchain.customName).resName(R.string.pers_item_custom_blockchain))
block.addItem(NumberUnit(from.MaxSignatures).resName(R.string.pers_item_max_signatures))
block.addItem(BoolUnit(from.createWalletB).resName(R.string.action_wallet_create))
return block
}
private fun signingMethod(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_signing_method))
block.addItem(BoolUnit(from.SigningMethod0).resName(R.string.pers_item_sign_tx_hashes))
block.addItem(BoolUnit(from.SigningMethod1).resName(R.string.pers_item_sign_raw_tx))
block.addItem(BoolUnit(from.SigningMethod2).resName(R.string.pers_item_sign_validated_tx_hashes))
block.addItem(BoolUnit(from.SigningMethod3).resName(R.string.pers_item_sign_validated_raw_tx))
block.addItem(BoolUnit(from.SigningMethod4).resName(R.string.pers_item_sign_validated_tx_hashes_with_iss_data))
block.addItem(BoolUnit(from.SigningMethod5).resName(R.string.pers_item_sign_validated_raw_tx_with_iss_data))
block.addItem(BoolUnit(from.SigningMethod6).resName(R.string.pers_item_sign_hash_ex))
return block
}
private fun signHashExProperties(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_sign_hash_ex_prop))
block.addItem(NumberUnit(from.pinLessFloorLimit).resName(R.string.pers_item_pin_less_floor_limit))
block.addItem(EditTextUnit(from.hexCrExKey).resName(R.string.pers_item_cr_ex_key))
block.addItem(BoolUnit(from.requireTerminalCertSignature).resName(R.string.pers_item_require_terminal_cert_sig))
block.addItem(BoolUnit(from.requireTerminalTxSignature).resName(R.string.pers_item_require_terminal_tx_sig))
block.addItem(BoolUnit(from.checkPIN3onCard).resName(R.string.pers_item_check_pin3_on_card))
return block
}
private fun denomination(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_denomination))
block.addItem(BoolUnit(from.writeOnPersonalization).resName(R.string.pers_item_write_on_personalize))
block.addItem(NumberUnit(from.denomination).resName(R.string.pers_item_denomination))
return block
}
private fun token(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_token))
block.addItem(BoolUnit(from.itsToken).resName(R.string.pers_item_its_token))
block.addItem(EditTextUnit(from.symbol).resName(R.string.pers_item_symbol))
block.addItem(EditTextUnit(from.contractAddress).resName(R.string.pers_item_contract_address))
block.addItem(NumberUnit(from.decimal).resName(R.string.pers_item_decimal))
return block
}
private fun productMask(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_product_mask))
block.addItem(BoolUnit(from.cardData.product_note).resName(R.string.pers_item_note))
block.addItem(BoolUnit(from.cardData.product_tag).resName(R.string.pers_item_tag))
block.addItem(BoolUnit(from.cardData.product_id_card).resName(R.string.pers_item_id_card))
return block
}
private fun settingsMask(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_settings_mask))
block.addItem(BoolUnit(from.isReusable).resName(R.string.pers_item_is_reusable))
block.addItem(BoolUnit(from.useActivation).resName(R.string.pers_item_need_activation))
block.addItem(BoolUnit(from.forbidPurgeWallet).resName(R.string.pers_item_forbid_purge))
block.addItem(BoolUnit(from.allowSelectBlockchain).resName(R.string.pers_item_allow_select_blockchain))
block.addItem(BoolUnit(from.useBlock).resName(R.string.pers_item_use_block))
block.addItem(BoolUnit(from.useOneCommandAtTime).resName(R.string.pers_item_one_apdu_at_once))
block.addItem(BoolUnit(from.useCVC).resName(R.string.pers_item_use_cvc))
block.addItem(BoolUnit(from.allowSwapPIN).resName(R.string.pers_item_allow_swap_pin))
block.addItem(BoolUnit(from.allowSwapPIN2).resName(R.string.pers_item_allow_swap_pin2))
block.addItem(BoolUnit(from.forbidDefaultPIN).resName(R.string.pers_item_forbid_default_pin))
block.addItem(BoolUnit(from.smartSecurityDelay).resName(R.string.pers_item_smart_security_delay))
block.addItem(BoolUnit(from.protectIssuerDataAgainstReplay).resName(R.string.pers_item_protect_issuer_data_against_replay))
block.addItem(BoolUnit(from.skipSecurityDelayIfValidatedByIssuer).resName(R.string.pers_item_skip_security_delay_if_validated))
block.addItem(BoolUnit(from.skipCheckPIN2andCVCIfValidatedByIssuer).resName(R.string.pers_item_skip_pin2_and_cvc_if_validated))
block.addItem(BoolUnit(from.skipSecurityDelayIfValidatedByLinkedTerminal).resName(R.string.pers_item_skip_security_delay_on_linked_terminal))
block.addItem(BoolUnit(from.restrictOverwriteIssuerDataEx).resName(R.string.pers_item_restrict_overwrite_ex_issuer_data))
return block
}
private fun settingsMaskProtocolEnc(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_settings_mask_protocol_enc))
block.addItem(BoolUnit(from.protocolAllowUnencrypted).resName(R.string.pers_item_allow_unencrypted))
block.addItem(BoolUnit(false).resName(R.string.pers_item_allow_fast_encryption))
return block
}
private fun settingsMaskNdef(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_settings_mask_ndef))
block.addItem(BoolUnit(from.useNDEF).resName(R.string.pers_item_use_ndef))
block.addItem(BoolUnit(from.useDynamicNDEF).resName(R.string.pers_item_dynamic_ndef))
block.addItem(BoolUnit(from.disablePrecomputedNDEF).resName(R.string.pers_item_disable_precomputed_ndef))
block.addItem(ListUnit(Helper.aarList(), from.NDEF[0].type).resName(R.string.pers_item_aar))
// block.addItem(TextItem(R.string.pers_item_custom_aar_package_name, from.ndef[0].value))
return block
}
private fun pins(from: TestJsonDto): Block {
val block = LinearBlock()
block.addItem(TextUnit().resName(R.string.pers_block_pins))
block.addItem(EditTextUnit(from.PIN).resName(R.string.pers_item_pin))
block.addItem(EditTextUnit(from.PIN2).resName(R.string.pers_item_pin2))
block.addItem(EditTextUnit(from.PIN3).resName(R.string.pers_item_pin3))
block.addItem(EditTextUnit(from.CVC).resName(R.string.pers_item_cvc))
block.addItem(ListUnit(Helper.pauseBeforePin(), from.pauseBeforePIN2).resName(R.string.pers_item_pause_before_pin2))
return block
}
private fun addPayload(block: Block, from: TestJsonDto) {
block.payload["count"] = from.count
block.payload["numberFormat"] = from.numberFormat
block.payload["issuerData"] = from.issuerData
block.payload["releaseVersion"] = from.releaseVersion
block.payload["issuerName"] = from.issuerName
}
}
internal class Helper {
companion object {
fun listOfCurves(): List<KeyValue> {
return mutableListOf(
KeyValue("secp256k1", "secp256k1"),
KeyValue("ed25519", "ed25519")
)
}
fun listOfBlockchain(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", "BTC/test"),
KeyValue("BTC", "BTC"),
KeyValue("BTC/test", "BTC/test"),
KeyValue("ETH", "ETH"),
KeyValue("ETH/test", "ETH/test"),
KeyValue("BCH", "BCH"),
KeyValue("BCH/test", "BCH/test"),
KeyValue("LTC", "LTC"),
KeyValue("XLM", "XLM"),
KeyValue("XLM/test", "XLM/test"),
KeyValue("RSK", "RSK"),
KeyValue("XPR", "XPR"),
KeyValue("CARDANO", "CARDANO")
)
}
fun aarList(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", ""),
KeyValue("Release APP", "com.some.release"),
KeyValue("Debug APP", "com.some.debug"),
KeyValue("None", "")
)
}
fun pauseBeforePin(): List<KeyValue> {
return mutableListOf(
KeyValue("immediately", 0),
KeyValue("2 seconds", 2000),
KeyValue("5 seconds", 5000),
KeyValue("15 seconds", 15000),
KeyValue("30 seconds", 30000),
KeyValue("1 minute", 60000),
KeyValue("2 minute", 120000)
)
}
fun ndef(ndef: MutableList<Ndef>): Map<String, Any> {
val map = mutableMapOf<String, Any>()
ndef.forEach { map[it.type] = it.value }
return map
}
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test
import com.tangem.tangemtest._arch.structure.base.Block
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
class BlockToJsonConverter : Converter<Block, TestJsonDto> {
override fun convert(from: Block): TestJsonDto = TestJsonDto()
}

View file

@ -0,0 +1,29 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.view_model
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.google.gson.Gson
import com.tangem.tangemtest._arch.structure.base.Block
import com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test.BlockToJsonConverter
import com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test.JsonBlockEnDe
import com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test.JsonToBlockConverter
import com.tangem.tangemtest.card_use_cases.ui.personalize.personalize_converter.json_test.TestJsonDto
/**
[REDACTED_AUTHOR]
*/
class PersonalizeViewModelFactory(private val jsonPersonalizeString: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(jsonPersonalizeString) as T
}
class PersonalizeViewModel(private val jsonPersonalizeString: String) : ViewModel() {
val ldBlockList: MutableLiveData<Block> by lazy { MutableLiveData(readJson()) }
private fun readJson(): Block {
val enDe = JsonBlockEnDe(JsonToBlockConverter(), BlockToJsonConverter())
val jsonDto = Gson().fromJson(jsonPersonalizeString, TestJsonDto::class.java)
return enDe.decode(jsonDto)
}
}

View file

@ -0,0 +1,62 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.widgets
import android.view.View
import android.view.ViewGroup
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.base.Block
import com.tangem.tangemtest._arch.structure.base.DataUnit
import com.tangem.tangemtest._arch.structure.base.Unit
import com.tangem.tangemtest._arch.structure.impl.*
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
/**
[REDACTED_AUTHOR]
*/
interface BlockWidget : ViewWidget
class EmptyWidget(parent: ViewGroup) : BlockWidget {
override val view: View = parent.inflate(getLayoutId(), parent, false)
override fun getLayoutId(): Int = R.layout.w_empty
}
abstract class BaseBlockWidget<D>(parent: ViewGroup) : BaseViewWidget(parent), BlockWidget
class LinearBlockWidget(
private val linearBlock: LinearBlock,
parent: ViewGroup
) : BaseBlockWidget<List<UnitWidget<*>>>(parent) {
override fun getLayoutId(): Int = R.layout.w_personilize_block
}
class WidgetBuilder {
fun build(unit: Unit, parent: ViewGroup): ViewWidget? {
return when (unit) {
is Block -> buildBlock(unit, parent)
is DataUnit<*> -> buildUnitItem(unit, parent)
else -> EmptyWidget(parent)
}
}
private fun buildBlock(block: Block, parent: ViewGroup): BlockWidget {
return when (block) {
is LinearBlock -> {
val linearBlock = LinearBlockWidget(block, parent)
block.getItems().forEach { build(it, linearBlock.view as ViewGroup) }
linearBlock
}
else -> EmptyWidget(parent)
}
}
private fun buildUnitItem(unit: DataUnit<*>, parent: ViewGroup): UnitWidget<*>? {
return when (unit) {
is TextUnit -> TextWidget(parent, unit)
is EditTextUnit -> EditTextWidget(parent, unit)
is NumberUnit -> NumberWidget(parent, unit)
is BoolUnit -> SwitchWidget(parent, unit)
is ListUnit -> SpinnerWidget(parent, unit)
else -> null
}
}
}

View file

@ -0,0 +1,64 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.widgets
import android.view.LayoutInflater
import android.view.View
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.base.DataUnit
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
interface ViewWidget : LayoutHolder {
val view: View
}
interface UnitWidget<D> : ViewWidget {
val unit: DataUnit<D>
}
abstract class BaseViewWidget(parent: ViewGroup) : ViewWidget {
override val view: View = inflateView(parent)
private fun inflateView(parent: ViewGroup): View {
var id = getLayoutId()
if (id == -1) id = R.layout.w_empty
val inflatedView = LayoutInflater.from(parent.context).inflate(id, parent, false)
parent.addView(inflatedView)
return inflatedView
}
}
interface DescriptionWidget {
fun toggleDescriptionVisibility()
}
interface ParamWidget<D> : UnitWidget<D>, DescriptionWidget
abstract class BaseParamWidget<D>(
parent: ViewGroup,
override val unit: DataUnit<D>
) : BaseViewWidget(parent), ParamWidget<D> {
override fun toggleDescriptionVisibility() {
val tvDescription = view.findViewById<TextView>(R.id.tv_description) ?: return
tvDescription.text = getDescription()
TransitionManager.beginDelayedTransition(tvDescription.parent as ViewGroup, AutoTransition())
tvDescription.visibility = unit.viewModel?.viewState?.descriptionVisibility ?: View.GONE
}
}
fun UnitWidget<*>.getResNameId(): Int {
val id = unit.payload["resName"] ?: R.string.unknown
return id as Int
}
fun UnitWidget<*>.getDescription(): String {
return stringOf(unit.payload["description"], isNull = "not found")
}

View file

@ -0,0 +1,128 @@
package com.tangem.tangemtest.card_use_cases.ui.personalize.widgets
import android.view.View
import android.view.ViewGroup
import android.widget.*
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.*
import ru.dev.gbixahue.eu4d.lib.android._android.views.afterTextChanged
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class TextWidget(parent: ViewGroup, private val textUnit: TextUnit) : BaseParamWidget<String>(parent, textUnit) {
private val tvName = view.findViewById<TextView>(R.id.tv_name)
init {
tvName.setText(getResNameId())
}
override fun getLayoutId(): Int = R.layout.w_personalize_item_text
}
class EditTextWidget(parent: ViewGroup, private val editTextUnit: EditTextUnit) : BaseParamWidget<String>(parent, editTextUnit) {
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
private val etItem = view.findViewById<TextInputEditText>(R.id.et_item)
init {
tilItem.hint = tilItem.context.getString(getResNameId())
etItem.setText(editTextUnit.viewModel?.data)
etItem.afterTextChanged { editTextUnit.viewModel?.updateData(it) }
}
override fun getLayoutId(): Int = R.layout.w_personalize_item_edit_text
}
class NumberWidget(parent: ViewGroup, private val numberUnit: NumberUnit) : BaseParamWidget<Number>(parent, numberUnit) {
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
private val etItem = view.findViewById<TextInputEditText>(R.id.et_item)
init {
tilItem.hint = tilItem.context.getString(getResNameId())
etItem.setText(stringOf(numberUnit.viewModel?.data))
etItem.afterTextChanged { numberUnit.viewModel?.updateData(getIntValue(it)) }
}
private fun getIntValue(value: String): Number? {
return if (value.isEmpty()) null else value.toInt()
}
override fun getLayoutId(): Int = R.layout.w_personalize_item_number
}
class SwitchWidget(parent: ViewGroup, private val boolUnit: BoolUnit) : BaseParamWidget<Boolean>(parent, boolUnit) {
private val switchItem = view.findViewById<Switch>(R.id.sw_item)
init {
switchItem.setText(getResNameId())
switchItem.isChecked = boolUnit.viewModel?.data ?: false
switchItem.setOnCheckedChangeListener { view, isChecked -> boolUnit.viewModel?.updateData(isChecked) }
}
override fun getLayoutId(): Int = R.layout.w_personalize_item_switch
}
class SpinnerWidget(parent: ViewGroup, private val listUnit: ListUnit) : BaseParamWidget<ModelHelper>(parent, listUnit) {
private val spItem = view.findViewById<Spinner>(R.id.sp_item)
private val spAdapter = SpItemAdapter(listUnit.viewModel?.data?.itemList)
init {
val name = view.findViewById<TextView>(R.id.tv_name)
name.setText(getResNameId())
spItem.adapter = spAdapter
spItem.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val data = listUnit.viewModel?.data ?: return
data.selectedItem = data.itemList[position]
listUnit.viewModel?.updateData(data)
}
}
}
override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner
}
class SpItemAdapter(list: List<KeyValue>?) : BaseAdapter() {
private val itemList: List<KeyValue> = list ?: listOf()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: parent.inflate(R.layout.vh_sp_item)
val tv = view.findViewById<TextView>(R.id.tv_sp_item)
tv.text = extractData(itemList[position])
return view
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: parent.inflate(R.layout.vh_sp_dropdown_item)
val tv = view.findViewById<TextView>(R.id.tv_sp_item)
tv.text = extractData(itemList[position])
return view
}
private fun extractData(item: KeyValue): String? = item.key
override fun getItem(position: Int): String = stringOf(itemList[position])
override fun getItemId(position: Int): Long = position.toLong()
override fun getCount(): Int = itemList.size
}

View file

@ -1,8 +0,0 @@
package com.tangem.tangemtest.commons
/**
[REDACTED_AUTHOR]
*/
interface OnBackPressHandler {
fun onBackPressed(): Boolean
}

View file

@ -1,8 +1,8 @@
package com.tangem.tangemtest.commons
import ru.dev.gbixahue.eu4d.lib.android.global.log.CLogger
import ru.dev.gbixahue.eu4d.lib.android.global.log.TagLogger
/**
[REDACTED_AUTHOR]
*/
class TangemLogger : CLogger("TangemDemo")
class TangemLogger : TagLogger("TangemDemo")

View file

@ -33,5 +33,6 @@ enum class ActionType(@StringRes val resName: Int) {
WriteIssuerExData(R.string.action_issuer_write_ex_data),
ReadUserData(R.string.action_user_read_data),
WriteUserData(R.string.action_user_write_data),
Personalize(R.string.personalize),
Unknown(R.string.unknown),
}

View file

@ -1,238 +1,238 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:text="Scan card" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_read_write"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<CheckBox
android:id="@+id/chb_with_ud"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User data" />
<CheckBox
android:id="@+id/chb_with_ud_protected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User protected data" />
<CheckBox
android:id="@+id/chb_with_pin2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With Pin2" />
<CheckBox
android:id="@+id/chb_with_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User counter" />
<CheckBox
android:id="@+id/chb_with_protected_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User protected counter" />
</LinearLayout>
android:orientation="vertical">
<Button
android:id="@+id/btn_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Write user data"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="205dp" />
<TextView
android:id="@+id/tv_write_result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Write result"
app:layout_constraintBottom_toBottomOf="@+id/btn_write"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/btn_write" />
<include
android:id="@+id/m_delimiter_h"
layout="@layout/m_divider_h"
android:layout_width="0dp"
android:layout_height="@dimen/delimiter_size"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_write" />
<Button
android:id="@+id/btn_read"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Read User Data"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/m_delimiter_h" />
<TextView
android:id="@+id/tv_read_result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Read result"
app:layout_constraintBottom_toBottomOf="@+id/btn_read"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/btn_read" />
android:id="@+id/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:text="Scan card" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="20dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_read"
app:layout_constraintVertical_bias="0.0">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/cl_read_write"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical"
app:layout_constraintGuide_begin="156dp" />
android:visibility="gone">
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Card Id:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User data:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView6" />
<CheckBox
android:id="@+id/chb_with_ud"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User data" />
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User protected data:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView7" />
<CheckBox
android:id="@+id/chb_with_ud_protected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User protected data" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User counter:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView8" />
<CheckBox
android:id="@+id/chb_with_pin2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With Pin2" />
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User protected counter:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView9" />
<CheckBox
android:id="@+id/chb_with_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User counter" />
<CheckBox
android:id="@+id/chb_with_protected_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="With User protected counter" />
</LinearLayout>
<Button
android:id="@+id/btn_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Write user data"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="205dp" />
<TextView
android:id="@+id/tv_write_result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Write result"
app:layout_constraintBottom_toBottomOf="@+id/btn_write"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/btn_write" />
<include
android:id="@+id/m_delimiter_h"
layout="@layout/m_divider_h"
android:layout_width="0dp"
android:layout_height="@dimen/delimiter_size"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_write" />
<Button
android:id="@+id/btn_read"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Read User Data"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/m_delimiter_h" />
<TextView
android:id="@+id/tv_read_result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Read result"
app:layout_constraintBottom_toBottomOf="@+id/btn_read"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/btn_read" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="20dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_read"
app:layout_constraintVertical_bias="0.0">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="156dp" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Card Id:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User data:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView6" />
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User protected data:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView7" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User counter:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView8" />
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="User protected counter:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView9" />
<TextView
android:id="@+id/tv_card_cid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:text="card id" />
<TextView
android:id="@+id/tv_card_cid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:text="card id" />
<TextView
android:id="@+id/tv_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_card_cid"
app:layout_constraintTop_toBottomOf="@+id/textView6"
tools:text="data" />
<TextView
android:id="@+id/tv_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_card_cid"
app:layout_constraintTop_toBottomOf="@+id/textView6"
tools:text="data" />
<TextView
android:id="@+id/tv_protected_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_data"
app:layout_constraintTop_toBottomOf="@+id/textView7"
tools:text="protected data" />
<TextView
android:id="@+id/tv_protected_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_data"
app:layout_constraintTop_toBottomOf="@+id/textView7"
tools:text="protected data" />
<TextView
android:id="@+id/tv_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_protected_data"
app:layout_constraintTop_toBottomOf="@+id/tv_protected_data"
tools:text="counter" />
<TextView
android:id="@+id/tv_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_protected_data"
app:layout_constraintTop_toBottomOf="@+id/tv_protected_data"
tools:text="counter" />
<TextView
android:id="@+id/tv_protected_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_counter"
app:layout_constraintTop_toBottomOf="@+id/tv_counter"
tools:text="protected counter" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_protected_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/tv_counter"
app:layout_constraintTop_toBottomOf="@+id/tv_counter"
tools:text="protected counter" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -13,4 +13,14 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_personalize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="@string/personalize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
</ScrollView>

View file

@ -1,116 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Old_MainActivity">
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_card_cid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:layout_marginBottom="80dp"
android:padding="16dp"
android:paddingBottom="48dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Old_MainActivity">
<Button
android:id="@+id/btn_scan"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="Read and verify card"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.25" />
<TextView
android:id="@+id/tv_card_cid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:layout_marginBottom="80dp"
android:padding="16dp"
android:paddingBottom="48dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_sign"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Sign transaction"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_scan" />
<Button
android:id="@+id/btn_scan"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="Read and verify card"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.25" />
<Button
android:id="@+id/btn_read_issuer_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Read issuer data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_sign" />
<Button
android:id="@+id/btn_sign"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Sign transaction"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_scan" />
<Button
android:id="@+id/btn_write_issuer_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Write issuer data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_read_issuer_data" />
<Button
android:id="@+id/btn_read_issuer_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Read issuer data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_sign" />
<Button
android:id="@+id/btn_read_issuer_extra_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Read issuer extra data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_write_issuer_data" />
<Button
android:id="@+id/btn_write_issuer_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Write issuer data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_read_issuer_data" />
<Button
android:id="@+id/btn_purge_wallet"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Purge wallet"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_read_issuer_extra_data" />
<Button
android:id="@+id/btn_read_issuer_extra_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Read issuer extra data"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_write_issuer_data" />
<Button
android:id="@+id/btn_create_wallet"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Create wallet"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_purge_wallet" />
<Button
android:id="@+id/btn_purge_wallet"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Purge wallet"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_read_issuer_extra_data" />
<Button
android:id="@+id/btn_read_write_user_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Read/Write user data"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_create_wallet" />
<Button
android:id="@+id/btn_create_wallet"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="Create wallet"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_purge_wallet" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn_read_write_user_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Read/Write user data"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_create_wallet" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="48dp"
xmlns:tools="http://schemas.android.com/tools"
android:background="?android:attr/selectableItemBackground">
<TextView

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_sp_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/def_indent"
android:paddingTop="@dimen/def_half_indent"
android:paddingEnd="@dimen/def_indent"
android:paddingBottom="@dimen/def_half_indent"
android:textColor="@android:color/black"
android:textSize="@dimen/unit_widget_text_size" />

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_sp_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="@dimen/def_half_indent"
android:textColor="@android:color/black"
android:textSize="@dimen/unit_widget_text_size" />

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp" />

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/def_indent"
android:layout_marginEnd="@dimen/def_indent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/def_indent"
android:paddingEnd="@dimen/def_indent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_item"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:boxBackgroundColor="@android:color/transparent"
app:endIconMode="clear_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|bottom"
android:paddingStart="0dp"
android:paddingEnd="@dimen/def_double_indent"
android:paddingBottom="4dp"
android:textSize="@dimen/unit_widget_text_size" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/def_indent"
android:paddingEnd="@dimen/def_indent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_item"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:boxBackgroundColor="@android:color/transparent"
app:endIconMode="clear_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|bottom"
android:inputType="number"
android:paddingStart="0dp"
android:paddingEnd="@dimen/def_double_indent"
android:paddingBottom="4dp"
android:textSize="@dimen/unit_widget_text_size" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/def_indent"
android:paddingEnd="@dimen/def_indent">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="@dimen/unit_widget_text_size_hint"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Name" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/sp_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/unit_widget_min_height"
android:paddingStart="@dimen/def_indent"
android:paddingEnd="@dimen/def_indent">
<Switch
android:id="@+id/sw_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="2dp"
android:textSize="@dimen/unit_widget_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/delimiter"
android:padding="@dimen/def_indent">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|bottom"
android:textSize="@dimen/unit_widget_text_size_block"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/def_half_indent"
android:orientation="vertical" />

View file

@ -40,20 +40,29 @@
<action
android:id="@+id/action_nav_entry_point_to_nav_wallet_purge"
app:destination="@id/nav_wallet_purge" />
<action
android:id="@+id/action_nav_entry_point_to_nav_personalize"
app:destination="@id/nav_personalize" />
</fragment>
<fragment
android:id="@+id/nav_scan"
android:name="com.tangem.tangemtest.card_use_cases.ui.ScanActionFragment"
android:name="com.tangem.tangemtest.card_use_cases.ui.card_action.ScanActionFragment"
android:label="@string/action_card_scan"
tools:layout="@layout/fg_action_card_scan" />
<fragment
android:id="@+id/nav_sign"
android:name="com.tangem.tangemtest.card_use_cases.ui.SignActionFragment"
android:name="com.tangem.tangemtest.card_use_cases.ui.card_action.SignActionFragment"
android:label="@string/action_card_sign"
tools:layout="@layout/fg_action_card_sign" />
<fragment
android:id="@+id/nav_personalize"
android:name="com.tangem.tangemtest.card_use_cases.ui.personalize.PersonalizeFragment"
android:label="@string/action_issuer_read_data"
tools:layout="@layout/fg_personalize" />
<fragment
android:id="@+id/nav_issuer_read_data"

View file

@ -7,6 +7,12 @@
<dimen name="def_indent">16dp</dimen>
<dimen name="def_double_indent">32dp</dimen>
<dimen name="unit_widget_min_height">52dp</dimen>
<dimen name="unit_widget_text_size_block">20sp</dimen>
<dimen name="unit_widget_text_size">16sp</dimen>
<dimen name="unit_widget_text_size_hint">12sp</dimen>
<dimen name="unit_widget_text_size_description">14sp</dimen>
<dimen name="indent_card_incoming_param_name_h">@dimen/def_indent</dimen>
<dimen name="indent_card_incoming_param_name_v">@dimen/def_indent</dimen>

View file

@ -14,4 +14,74 @@
<string name="action_user_write_data">Write user data</string>
<string name="current_card_id">"Current card ID: "</string>
<string name="unknown">unknown</string>
<string name="personalize">Personalize</string>
<string name="pers_block_card_number">Card number</string>
<string name="pers_block_common">Common</string>
<string name="pers_block_signing_method">Signing method</string>
<string name="pers_block_sign_hash_ex_prop">Sign hash external properties</string>
<string name="pers_block_denomination">Denomination</string>
<string name="pers_block_token">Token</string>
<string name="pers_block_product_mask">Product mask</string>
<string name="pers_block_settings_mask">Settings mask</string>
<string name="pers_block_settings_mask_protocol_enc">Settings mask protocol encryption</string>
<string name="pers_block_settings_mask_ndef">Settings mask ndef</string>
<string name="pers_block_pins">Pins</string>
<string name="pers_item_series">Series</string>
<string name="pers_item_number">Number</string>
<string name="pers_item_curve">Curve</string>
<string name="pers_item_blockchain">Blockchain</string>
<string name="pers_item_custom_blockchain">Custom blockchain</string>
<string name="pers_item_max_signatures">Max signatures</string>
<string name="pers_item_create_wallet">Create wallet</string>
<string name="pers_item_sign_tx_hashes">Sign TX hashes (0)</string>
<string name="pers_item_sign_raw_tx">Sign raw TX (1)</string>
<string name="pers_item_sign_validated_tx_hashes">Sign validated TX hashes (2)</string>
<string name="pers_item_sign_validated_raw_tx">Sign validated raw TX (3)</string>
<string name="pers_item_sign_validated_tx_hashes_with_iss_data">Sign validated TX hashes with issuer data (4)</string>
<string name="pers_item_sign_validated_raw_tx_with_iss_data">Sign validated raw TX with issuer data (5)</string>
<string name="pers_item_sign_hash_ex">Sign hash external (6)</string>
<string name="pers_item_pin_less_floor_limit">Pin-less floor limit</string>
<string name="pers_item_cr_ex_key">Cr(ypto)Ex(tract) key</string>
<string name="pers_item_require_terminal_cert_sig">Require terminal Cert signature</string>
<string name="pers_item_require_terminal_tx_sig">Require terminal TX signature</string>
<string name="pers_item_check_pin3_on_card">Check PIN3 on card</string>
<string name="pers_item_write_on_personalize">Write on personalize</string>
<string name="pers_item_denomination">Denomination</string>
<string name="pers_item_its_token">It\'s token</string>
<string name="pers_item_symbol">Symbol</string>
<string name="pers_item_contract_address">Contract address</string>
<string name="pers_item_decimal">Decimal</string>
<string name="pers_item_note">Note</string>
<string name="pers_item_tag">Tag</string>
<string name="pers_item_id_card">ID card</string>
<string name="pers_item_is_reusable">Is reusable</string>
<string name="pers_item_need_activation">Need activation</string>
<string name="pers_item_forbid_purge">Forbid purge</string>
<string name="pers_item_allow_select_blockchain">Allow select blockchain</string>
<string name="pers_item_use_block">Use block</string>
<string name="pers_item_one_apdu_at_once">One APDU at once</string>
<string name="pers_item_use_cvc">Use CVC</string>
<string name="pers_item_allow_swap_pin">Allow swap PIN</string>
<string name="pers_item_allow_swap_pin2">Allow swap PIN2</string>
<string name="pers_item_forbid_default_pin">Forbid default PIN</string>
<string name="pers_item_smart_security_delay">Smart Security Delay</string>
<string name="pers_item_protect_issuer_data_against_replay">Protect issuer data against replay</string>
<string name="pers_item_skip_security_delay_if_validated">Security delay if validated</string>
<string name="pers_item_skip_pin2_and_cvc_if_validated">Skip PIN2 and CVC if validated</string>
<string name="pers_item_skip_security_delay_on_linked_terminal">Skip security delay on linked terminal</string>
<string name="pers_item_restrict_overwrite_ex_issuer_data">Restrict overwrite extra issuer data</string>
<string name="pers_item_allow_unencrypted">Allow unencrypted</string>
<string name="pers_item_allow_fast_encryption">Allow \'FAST\' encryption</string>
<string name="pers_item_use_ndef">Use NDEF</string>
<string name="pers_item_dynamic_ndef">Dynamic NDEF</string>
<string name="pers_item_disable_precomputed_ndef">Disable precomputed NDEF</string>
<string name="pers_item_aar">AAR</string>
<string name="pers_item_custom_aar_package_name">Custom AAR package name</string>
<string name="pers_item_pin">PIN</string>
<string name="pers_item_pin2">PIN2</string>
<string name="pers_item_pin3">PIN3</string>
<string name="pers_item_cvc">CVC</string>
<string name="pers_item_pause_before_pin2">Pause before PIN2</string>
</resources>