Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-13 21:58:27 +03:00
parent 91835911f1
commit 5aa06a3074
25 changed files with 565 additions and 97 deletions

View file

@ -5,7 +5,12 @@ import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
interface Item {
interface UpdateBy<B>{
fun update(value: B)
}
interface Item: UpdateBy<Item> {
val id: Id
var parent: Item?
var viewModel: ItemViewModel
@ -36,4 +41,7 @@ open class BaseItem(
override var parent: Item? = null
override fun update(value: Item) {
viewModel.update(value.viewModel)
}
}

View file

@ -49,4 +49,8 @@ open class SimpleItemGroup(
ILog.d(this, "clear $id")
itemList.clear()
}
override fun update(value: Item) {
// nothing to do
}
}

View file

@ -16,7 +16,7 @@ class KeyValue(val key: String, val value: Any)
class ViewState(
isVisible: Boolean? = null,
bgColor: Int? = -1
) {
) : UpdateBy<ViewState> {
class State<T>(
stateValue: T,
@ -41,9 +41,15 @@ class ViewState(
val states = listOf(isVisibleState, backgroundColor, descriptionVisibility)
states.forEach { it.preventSameChanges = isPrevented }
}
override fun update(value: ViewState) {
isVisibleState = value.isVisibleState
backgroundColor = value.backgroundColor
descriptionVisibility = value.descriptionVisibility
}
}
interface ItemViewModel : PayloadHolder {
interface ItemViewModel : PayloadHolder, UpdateBy<ItemViewModel> {
val viewState: ViewState
var data: Any?
var defaultData: Any?
@ -92,6 +98,14 @@ open class BaseItemViewModel(
this.data = data
onDataUpdated = callback
}
override fun update(value: ItemViewModel) {
viewState.update(value.viewState)
defaultData = value.defaultData
data = value.data
payload.clear()
payload.putAll(value.payload)
}
}
class ListViewModel(

View file

@ -53,7 +53,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
val switchMenu = menu.findItem(R.id.action_favorite)
val switchMenu = menu.findItem(R.id.action_toggle_description_visibility)
(switchMenu.actionView as? SwitchCompat)?.let {
it.setOnCheckedChangeListener { buttonView, isChecked -> vm.switchToggled(isChecked) }
}

View file

@ -4,6 +4,13 @@ package com.tangem.tangemtest.commons
[REDACTED_AUTHOR]
*/
interface Store<M> {
fun save(config: M)
fun save(value: M)
fun restore(): M
}
interface KeyedStore<M> {
fun save(key: String, value: M)
fun restore(key: String): M
fun restoreAll(): Map<String, M>
fun delete(key: String)
}

View file

@ -0,0 +1,76 @@
package com.tangem.tangemtest.commons
import android.app.Activity
import android.app.Dialog
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AlertDialog
class DialogController {
var onDismissCallback: (() -> Unit)? = null
var onShowCallback: (() -> Unit)? = null
var view: View? = null
private var rawDialog: Dialog? = null
private var inShowingProcess = false
private var inDismissingProcess = false
private var autoReleaseOnDismiss = false
fun createAlert(context: Activity, resLayout: Int): AlertDialog {
view = LayoutInflater.from(context).inflate(resLayout, null)
rawDialog = AlertDialog.Builder(context).setView(view).create().apply {
setOnShowListener { onShow() }
setOnDismissListener { onDismiss() }
}
return rawDialog as AlertDialog
}
fun set(dialog: Dialog) {
rawDialog = dialog
dialog.setOnShowListener { onShow() }
dialog.setOnDismissListener { onDismiss() }
}
private fun onShow() {
inShowingProcess = false
onShowCallback?.invoke()
}
private fun onDismiss() {
inDismissingProcess = false
if (autoReleaseOnDismiss) release()
onDismissCallback?.invoke()
}
fun show() {
val dialog = rawDialog ?: return
if (inShowingProcess) return
if (dialog.isShowing) return
inShowingProcess = true
dialog.show()
}
fun dismiss(autoRelease: Boolean = true) {
val dialog = rawDialog ?: return
if (inDismissingProcess) return
if (!dialog.isShowing) return
autoReleaseOnDismiss = autoRelease
inDismissingProcess = true
dialog.dismiss()
}
fun release() {
onDismissCallback = null
onShowCallback = null
inShowingProcess = false
inDismissingProcess = false
autoReleaseOnDismiss = false
view = null
rawDialog = null
}
}

View file

@ -37,7 +37,7 @@ class PersonalizeAction : BaseAction() {
val acquirer = DefaultPersonalizationParams.acquirer()
val manufacturer = DefaultPersonalizationParams.manufacturer()
val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig())
val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig.default())
val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig)
attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) {

View file

@ -25,6 +25,7 @@ interface ItemsManager : PayloadHolder {
fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback)
fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)?
fun attachPayload(payload: Payload)
fun updateByItemList(list: List<Item>)
}
interface PayloadKey {

View file

@ -6,6 +6,7 @@ import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findItem
import com.tangem.tangemtest._arch.structure.abstraction.iterate
import com.tangem.tangemtest.ucase.domain.actions.Action
import com.tangem.tangemtest.ucase.domain.actions.AttrForAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
@ -47,6 +48,10 @@ open class BaseItemsManager(protected val action: Action) : ItemsManager, Lifecy
this.changeConsequence = consequence
}
override fun updateByItemList(list: List<Item>) {
list.iterate { itemList.findItem(it.id)?.update(it) }
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}

View file

@ -29,7 +29,7 @@ class PersonalizationItemsManager(
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
val config = converter.convert(itemList, PersonalizationConfig())
val config = converter.convert(itemList, PersonalizationConfig.default())
store.save(config)
}
}

View file

@ -12,6 +12,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.tangem.TangemSdk
import com.tangem.commands.Card
import com.tangem.commands.CommandResponse
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
@ -87,39 +88,45 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView {
protected open fun subscribeToViewModelChanges() {
Log.d(this, "subscribeToViewModelChanges")
listenResponse()
listenResponseData()
listenResponseCardData()
listenError()
listenChangedItems()
listenDescriptionSwitchChanges()
}
private fun listenResponse() {
actionVM.seResponse.observe(viewLifecycleOwner, Observer {
Log.d(this, "listen response: $it")
mainActivityVM.changeResponseEvent(it)
Log.d(this, "handle response: $it")
handleResponse(it)
})
}
protected open fun listenResponseData() {
actionVM.seResponseData.observe(viewLifecycleOwner, Observer {
Log.d(this, "listen responseData: $it")
navigateTo(R.id.action_nav_card_action_to_response_screen)
Log.d(this, "handle responseData: $it")
handleResponseData(it)
})
}
protected open fun listenResponseCardData() {
actionVM.seResponseCardData.observe(viewLifecycleOwner, Observer {
Log.d(this, "listen responseCardData: $it")
responseCardDataHandled(it)
Log.d(this, "handle responseCardData: $it")
handleResponseCardData(it)
})
actionVM.seError.observe(viewLifecycleOwner, Observer {
Log.d(this, "handle error: $it")
handleError(it)
})
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
Log.d(this, "handle descriptionVisibilityState: $it")
handleDescriptionSwitchChanges(it)
})
listenChangedItems()
}
protected open fun responseCardDataHandled(card: Card?) {}
protected open fun handleResponse(response: CommandResponse) {
mainActivityVM.changeResponseEvent(response)
}
protected open fun listenError() {
actionVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) })
protected open fun handleResponseData(response: CommandResponse) {
navigateTo(R.id.action_nav_card_action_to_response_screen)
}
protected open fun handleResponseCardData(card: Card) {}
protected open fun handleError(error: String) {
showSnackbar(error)
}
protected open fun handleDescriptionSwitchChanges(descriptionVisibilityState: Boolean) {
actionVM.toggleDescriptionVisibility(descriptionVisibilityState)
}
@Deprecated("Start to use itemViewModel")
@ -132,12 +139,6 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView {
})
}
protected open fun listenDescriptionSwitchChanges() {
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
actionVM.toggleDescriptionVisibility(it)
})
}
private fun inflateParamView(where: ViewGroup): ViewGroup {
val inflater = LayoutInflater.from(where.context)
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)

View file

@ -5,22 +5,46 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import com.google.gson.Gson
import com.tangem.tangemtest.AppTangemDemo
import com.tangem.tangemtest.commons.KeyedStore
import com.tangem.tangemtest.commons.Store
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
class PersonalizationConfigStore(context: Context) : Store<PersonalizationConfig> {
class PersonalizationConfigStore(context: Context) : Store<PersonalizationConfig>, KeyedStore<PersonalizationConfig> {
private val key = "personalization_config"
private val sharedPreferencesKey = "personalization_presets"
private val defaultKey = "default"
private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences()
private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences(sharedPreferencesKey)
private val gson: Gson = Gson()
override fun save(config: PersonalizationConfig) {
sp.edit(true) { putString(key, gson.toJson(config)) }
override fun save(value: PersonalizationConfig) {
save(defaultKey, value)
}
override fun restore(): PersonalizationConfig {
val json = sp.getString(key, gson.toJson(PersonalizationConfig()))
return gson.fromJson(json, PersonalizationConfig::class.java)
override fun restore(): PersonalizationConfig = restore(defaultKey)
override fun save(key: String, value: PersonalizationConfig) {
sp.edit(true) { putString(key, toJson(value)) }
}
override fun restore(key: String): PersonalizationConfig {
val json = sp.getString(key, toJson(getDefaultConfig()))
return fromJson(json!!)
}
override fun restoreAll(): Map<String, PersonalizationConfig> {
val map = mutableMapOf<String, PersonalizationConfig>()
sp.all.forEach { map[it.key] = fromJson(it.value as String) }
return map.toSortedMap()
}
override fun delete(key: String) {
sp.edit(true) { remove(key) }
}
private fun getDefaultConfig(): PersonalizationConfig = PersonalizationConfig.default()
private fun toJson(value: PersonalizationConfig): String = gson.toJson(value)
private fun fromJson(json: String): PersonalizationConfig = gson.fromJson(json, PersonalizationConfig::class.java)
}

View file

@ -6,20 +6,19 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto
class PersonalizationConfig {
// Card number
var series = "BB"
var startNumber: Long = 300000000000
var batchId = "ffff"
var series = ""
var startNumber: Long = 0
var batchId = ""
// Common
var curveID = "ed25519"
var blockchain = "BTC/test"
var curveID = ""
var blockchain = ""
var blockchainCustom = ""
var MaxSignatures: Long = 999999
var createWallet = true
var MaxSignatures: Long = 0
var createWallet = false
// Signing method
var SigningMethod0 = true
var SigningMethod0 = false
var SigningMethod1 = false
var SigningMethod2 = false
var SigningMethod3 = false
@ -27,19 +26,16 @@ class PersonalizationConfig {
var SigningMethod5 = false
var SigningMethod6 = false
// Sign hash external properties
var pinLessFloorLimit: Long = 100000
var hexCrExKey = "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF"
var pinLessFloorLimit: Long = 0
var hexCrExKey = ""
var requireTerminalTxSignature = false
var requireTerminalCertSignature = false
var checkPIN3onCard = true
var checkPIN3onCard = false
// Denomination
var writeOnPersonalization = false
var denomination: Long = 1000000
var denomination: Long = 0
// Token
var itsToken = false
@ -47,49 +43,125 @@ class PersonalizationConfig {
var contractAddress = ""
var decimal: Long = 0
var cardData = CardData()
// Settings mask
var isReusable = true
var isReusable = false
var useActivation = false
var forbidPurgeWallet = false
var allowSelectBlockchain = false
var useBlock = false
var oneApdu = false
var useCVC = false
var allowSwapPIN = true
var allowSwapPIN2 = true
var allowSwapPIN = false
var allowSwapPIN2 = false
var forbidDefaultPIN = false
var smartSecurityDelay = true
var protectIssuerDataAgainstReplay = true
var skipSecurityDelayIfValidatedByIssuer = true
var skipCheckPIN2andCVCIfValidatedByIssuer = true
var skipSecurityDelayIfValidatedByLinkedTerminal = true
var smartSecurityDelay = false
var protectIssuerDataAgainstReplay = false
var skipSecurityDelayIfValidatedByIssuer = false
var skipCheckPIN2andCVCIfValidatedByIssuer = false
var skipSecurityDelayIfValidatedByLinkedTerminal = false
var restrictOverwriteIssuerDataEx = false
// Settings mask - protocol encryption
var protocolAllowUnencrypted = true
var protocolAllowStaticEncryption = true
var protocolAllowUnencrypted = false
var protocolAllowStaticEncryption = false
var useNDEF = true
var useDynamicNDEF = true
var useNDEF = false
var useDynamicNDEF = false
var disablePrecomputedNDEF = false
var aar = "com.tangem.wallet"
var aar = ""
var aarCustom = ""
var uri = "https://tangem.com"
var uri = ""
// Pins
var PIN = "000000"
var PIN2 = "000"
var PIN = ""
var PIN2 = ""
var PIN3 = ""
var CVC = "000"
var pauseBeforePIN2: Long = 5000L
var CVC = ""
var pauseBeforePIN2: Long = 0
companion object {
fun default(): PersonalizationConfig {
return PersonalizationConfig().apply {
// Card number
series = "BB"
startNumber = 300000000000L
batchId = "ffff"
// Common
curveID = "ed25519"
blockchain = "BTC/test"
blockchainCustom = ""
MaxSignatures = 999999L
createWallet = true
// Signing method
SigningMethod0 = true
SigningMethod1 = false
SigningMethod2 = false
SigningMethod3 = false
SigningMethod4 = false
SigningMethod5 = false
SigningMethod6 = false
// Sign hash external properties
pinLessFloorLimit = 100000L
hexCrExKey = "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF"
requireTerminalTxSignature = false
requireTerminalCertSignature = false
checkPIN3onCard = true
// Denomination
writeOnPersonalization = false
denomination = 1000000L
// Token
itsToken = false
symbol = ""
contractAddress = ""
decimal = 0L
cardData = CardData()
// Settings mask
isReusable = true
useActivation = false
forbidPurgeWallet = false
allowSelectBlockchain = false
useBlock = false
oneApdu = false
useCVC = false
allowSwapPIN = true
allowSwapPIN2 = true
forbidDefaultPIN = false
smartSecurityDelay = true
protectIssuerDataAgainstReplay = true
skipSecurityDelayIfValidatedByIssuer = true
skipCheckPIN2andCVCIfValidatedByIssuer = true
skipSecurityDelayIfValidatedByLinkedTerminal = true
restrictOverwriteIssuerDataEx = false
// Settings mask - protocol encryption
protocolAllowUnencrypted = true
protocolAllowStaticEncryption = true
useNDEF = true
useDynamicNDEF = true
disablePrecomputedNDEF = false
aar = "com.tangem.wallet"
aarCustom = ""
uri = "https://tangem.com"
// Pins
PIN = "000000"
PIN2 = "000"
PIN3 = ""
CVC = "000"
pauseBeforePIN2 = 5000L
}
}
}
}
class CardData {

View file

@ -1,11 +1,14 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.Fade
import androidx.transition.TransitionManager
import com.tangem.commands.Card
@ -13,7 +16,9 @@ import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.StringId
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.SafeValueChanged
import com.tangem.tangemtest._arch.widget.WidgetBuilder
import com.tangem.tangemtest.commons.DialogController
import com.tangem.tangemtest.commons.view.MultiActionView
import com.tangem.tangemtest.commons.view.ViewAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
@ -24,6 +29,9 @@ import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore
import com.tangem.tangemtest.ucase.variants.personalize.ui.presets.PersonalizationPresetManager
import com.tangem.tangemtest.ucase.variants.personalize.ui.presets.PersonalizationPresetView
import com.tangem.tangemtest.ucase.variants.personalize.ui.presets.RvPresetNamesAdapter
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizationItemBuilder
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
@ -33,12 +41,17 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postWork
/**
[REDACTED_AUTHOR]
*/
class PersonalizationFragment : BaseCardActionFragment() {
class PersonalizationFragment : BaseCardActionFragment(), PersonalizationPresetView {
override val itemsManager: ItemsManager by lazy { PersonalizationItemsManager(PersonalizationConfigStore(requireContext())) }
override fun getLayoutId(): Int = R.layout.fg_base_action_layout
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
setHasOptionsMenu(true)
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(itemsManager as PersonalizationItemsManager)
@ -102,6 +115,28 @@ class PersonalizationFragment : BaseCardActionFragment() {
contentContainer.addView(btnContainer)
}
override fun handleResponseCardData(card: Card) {
super.handleResponseCardData(card)
navigateTo(R.id.action_nav_card_action_to_response_screen)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.setGroupVisible(R.id.menu_group_personalization_preset, true)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val store = PersonalizationConfigStore(requireContext())
val presetManager = PersonalizationPresetManager(itemsManager, store, this)
when (item.itemId) {
R.id.action_reset -> presetManager.resetToDefault()
R.id.action_save -> presetManager.savePreset()
R.id.action_load -> presetManager.loadPreset()
else -> return super.onOptionsItemSelected(item)
}
return true
}
override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) {
super.showSnackbar(id) {
when (id) {
@ -112,8 +147,31 @@ class PersonalizationFragment : BaseCardActionFragment() {
}
}
override fun responseCardDataHandled(card: Card?) {
super.responseCardDataHandled(card)
navigateTo(R.id.action_nav_card_action_to_response_screen)
override fun showSavePresetDialog(onOk: SafeValueChanged<String>) {
val dlgController = DialogController()
val dlg = dlgController.createAlert(requireActivity(), R.layout.dlg_personalization_preset_save)
dlg.setTitle(R.string.menu_preset_save)
dlg.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.btn_cancel)) { dialog, which -> }
dlg.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.btn_ok)) { dialog, which ->
val tvName = dlgController.view?.findViewById<EditText>(R.id.et_item) ?: return@setButton
val name = tvName.text.toString()
if (name.isEmpty()) showSnackbar("Not saved")
else onOk.invoke(name)
}
dlgController.show()
}
override fun showLoadPresetDialog(namesList: List<String>, onChoose: SafeValueChanged<String>, onDelete: SafeValueChanged<String>) {
val dlgController = DialogController()
val dlg = dlgController.createAlert(requireActivity(), R.layout.dlg_personalization_preset_load)
dlg.setTitle(R.string.menu_preset_load)
val rvPresetNames: RecyclerView = dlgController.view?.findViewById(R.id.recycler_view) ?: return
rvPresetNames.layoutManager = LinearLayoutManager(context)
rvPresetNames.adapter = RvPresetNamesAdapter({
onChoose(it)
dlgController.dismiss()
}, onDelete).apply { setItemList(namesList.toMutableList()) }
dlgController.show()
}
}

View file

@ -0,0 +1,47 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.presets
import com.tangem.tangemtest.R
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
class PersonalizationPresetManager(
private val itemsManager: ItemsManager,
private val store: PersonalizationConfigStore,
private val view: PersonalizationPresetView
) {
fun resetToDefault() {
val config = PersonalizationConfig.default()
val converter = PersonalizationConfigConverter()
itemsManager.updateByItemList(converter.convert(config))
store.save(config)
}
fun loadPreset() {
val presets = store.restoreAll()
val namesList = presets.map { it.key }.toMutableList()
if (namesList.size <= 1) {
view.showSnackbar(R.string.error_nothing_to_load)
return
}
namesList.removeAt(0)
view.showLoadPresetDialog(namesList, {
val converter = PersonalizationConfigConverter()
val config = store.restore(it)
itemsManager.updateByItemList(converter.convert(config))
}, {
store.delete(it)
})
}
fun savePreset() {
view.showSavePresetDialog { name ->
val converter = PersonalizationConfigConverter()
val config = converter.convert(itemsManager.getItems(), PersonalizationConfig.default())
store.save(name, config)
}
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.presets
import com.tangem.tangemtest._arch.structure.abstraction.SafeValueChanged
import com.tangem.tangemtest.ucase.tunnel.SnackbarHolder
interface PersonalizationPresetView : SnackbarHolder {
fun showSavePresetDialog(onOk: SafeValueChanged<String>)
fun showLoadPresetDialog(namesList: List<String>, onChoose: SafeValueChanged<String>, onDelete: SafeValueChanged<String>)
}

View file

@ -0,0 +1,47 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.presets
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.SafeValueChanged
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvBaseAdapter
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvVH
/**
[REDACTED_AUTHOR]
*/
class RvPresetNamesAdapter(
private val onItemClicked: SafeValueChanged<String>,
private val onDeleteClicked: SafeValueChanged<String>
) : RvBaseAdapter<PresetNameVH, String>() {
override fun onBindViewHolder(holder: PresetNameVH, position: Int) {
holder.bindData(itemList[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PresetNameVH {
val view = parent.inflate<View>(R.layout.vh_personalization_preset_name, false)
return PresetNameVH(view, onItemClicked, { position, value ->
onDeleteClicked(value)
itemList.removeAt(position)
this.notifyItemRemoved(position)
})
}
}
class PresetNameVH(
itemView: View,
private val onItemClicked: SafeValueChanged<String>,
private val onDeleteClicked: (Int, String) -> Unit
) : RvVH<String>(itemView) {
private val tvName = itemView.findViewById<TextView>(R.id.tv_name)
private val btnDelete = itemView.findViewById<Button>(R.id.btn_delete)
override fun onDataBound(data: String) {
tvName.text = data
tvName.isClickable = false
itemView.setOnClickListener { onItemClicked(data) }
btnDelete.setOnClickListener { onDeleteClicked(absoluteAdapterPosition, data) }
}
}

View file

@ -14,7 +14,7 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
abstract class ResponseWidget(parent: ViewGroup, item: Item): DescriptionWidget(parent, item) {
abstract class ResponseWidget(parent: ViewGroup, item: Item) : DescriptionWidget(parent, item) {
init {
view.setOnClickListener {

View file

@ -47,8 +47,8 @@ class ScanActionFragment : BaseCardActionFragment() {
actionFab.setOnClickListener { actionVM.invokeMainAction() }
}
override fun responseCardDataHandled(card: Card?) {
super.responseCardDataHandled(card)
override fun handleResponseCardData(card: Card) {
super.handleResponseCardData(card)
navigateTo(R.id.action_nav_card_action_to_response_screen)
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/def_indent"/>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_item"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
app:boxBackgroundColor="@android:color/transparent"
android:hint="Enter a preset name"
tools:hint="Field name">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Some text field" />
</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>

View file

@ -0,0 +1,32 @@
<?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="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_name"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btn_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:hint="Field name" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:text="@string/btn_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/action_share"
@ -10,9 +11,26 @@
app:showAsAction="always" />
<item
android:id="@+id/action_favorite"
android:id="@+id/action_toggle_description_visibility"
android:title=""
app:actionLayout="@layout/menu_item_switch"
app:showAsAction="always" />
<group
android:id="@+id/menu_group_personalization_preset"
android:visible="false"
tools:visible="true">
<item
android:id="@+id/action_reset"
android:title="@string/menu_preset_reset" />
<item
android:id="@+id/action_save"
android:title="@string/menu_preset_save" />
<item
android:id="@+id/action_load"
android:title="@string/menu_preset_load" />
</group>
</menu>

View file

@ -1,14 +1,21 @@
<resources>
<string name="app_name">Tangem Development Kit</string>
<string name="unknown">unknown</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="btn_delete">Delete</string>
<string name="btn_ok">OK</string>
<string name="btn_cancel">Cancel</string>
<string name="btn_save">Save</string>
<string name="btn_load">Load</string>
<string name="fg_name_entry_point">@string/app_name</string>
<string name="fg_name_response_scan">Scan response</string>
<string name="fg_name_response_sign">Sign response</string>
<string name="fg_name_response_personalization">Personalization response</string>
<string name="fg_name_response_depersonalization">Depersonalization response</string>
<string name="unknown">unknown</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="switch_description">Docs</string>
<string name="card_error_not_personalized">Your card hasn\'t been personalized yet. You need to run personalize command first.</string>

View file

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="menu_preset_reset">Reset configuration to default</string>
<string name="menu_preset_save">Save configuration</string>
<string name="menu_preset_load">Load configuration</string>
<string name="error_nothing_to_load">Nothing to load</string>
<string name="personalize">Personalize</string>
<string name="depersonalize">Depersonalize</string>
<string name="pers_block_card_number">Card number</string>