Updated on 2026-08-14
This commit is contained in:
parent
1d71266378
commit
8a6f9f3ff6
48 changed files with 1752 additions and 503 deletions
|
|
@ -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'
|
||||
}
|
||||
|
|
|
|||
55
tangem-demo/src/main/assets/personalize_default.json
Normal file
55
tangem-demo/src/main/assets/personalize_default.json
Normal 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
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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>>
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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?
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tangemtest._arch.structure.impl
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.base.ListUnitBlock
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class LinearBlock : ListUnitBlock()
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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>)
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ class ParamsManagerFactory : BaseTypedHolder<ActionType, ParamsManager>() {
|
|||
return ParamsManagerFactory().apply {
|
||||
register(ActionType.Scan, ScanParamsManager())
|
||||
register(ActionType.Sign, SignParamsManager())
|
||||
register(ActionType.Personalize, PersonalizeParamsManager())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.tangemtest.commons
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface OnBackPressHandler {
|
||||
fun onBackPressed(): Boolean
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
@ -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),
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
13
tangem-demo/src/main/res/layout/fg_personalize.xml
Normal file
13
tangem-demo/src/main/res/layout/fg_personalize.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
11
tangem-demo/src/main/res/layout/vh_sp_dropdown_item.xml
Normal file
11
tangem-demo/src/main/res/layout/vh_sp_dropdown_item.xml
Normal 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" />
|
||||
9
tangem-demo/src/main/res/layout/vh_sp_item.xml
Normal file
9
tangem-demo/src/main/res/layout/vh_sp_item.xml
Normal 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" />
|
||||
4
tangem-demo/src/main/res/layout/w_empty.xml
Normal file
4
tangem-demo/src/main/res/layout/w_empty.xml
Normal 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" />
|
||||
18
tangem-demo/src/main/res/layout/w_field_description.xml
Normal file
18
tangem-demo/src/main/res/layout/w_field_description.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
20
tangem-demo/src/main/res/layout/w_personalize_item_text.xml
Normal file
20
tangem-demo/src/main/res/layout/w_personalize_item_text.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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>
|
||||
6
tangem-demo/src/main/res/layout/w_personilize_block.xml
Normal file
6
tangem-demo/src/main/res/layout/w_personilize_block.xml
Normal 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" />
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue