Updated on 2026-08-14

This commit is contained in:
Tangem 2020-03-13 12:17:13 +03:00
parent 586dc1a563
commit bd91231803
42 changed files with 963 additions and 343 deletions

View file

@ -47,8 +47,8 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.2.1"
implementation "androidx.navigation:navigation-ui-ktx:2.2.1"
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha01"
implementation "com.google.android.material:material:1.2.0-alpha05"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.github.gbIxaHue:eu4d:0.2.3'
}

View file

@ -1,14 +1,21 @@
package com.tangem.tangemtest
import android.app.Application
import com.tangem.tangemtest.card_use_cases.CardContext
import com.tangem.tangemtest.commons.DiManager
import com.tangem.tangemtest.commons.TangemLogger
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class AppTangemDemo : Application(), DiManager {
private val cardContext: CardContext = CardContext()
class AppTangemDemo : Application() {
override fun getCardContext(): CardContext = cardContext
}
override fun onCreate() {
super.onCreate()
setLogger()
}
private fun setLogger() {
Log.setLogger(TangemLogger())
}
}

View file

@ -7,14 +7,13 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.CardContext
import com.tangem.tangemtest.card_use_cases.actions.Action
import com.tangem.tangemtest.commons.NavigateAction
import com.tangem.tangemtest.commons.Action
import com.tangem.tangemtest.commons.NavigateOptions
import com.tangem.tangemtest.commons.getDefaultNavigationOptions
import com.tangem.tangemtest.commons.getDiManager
import kotlinx.android.synthetic.main.fg_entry_point.*
/**
@ -31,32 +30,41 @@ class EntryPointFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = activity ?: throw Exception("Fragment isn't associated with activity ")
val cardContext = getDiManager().getCardContext().init(activity)
initRecyclerView(cardContext)
initRecyclerView()
}
private fun initRecyclerView(cardContext: CardContext) {
private fun initRecyclerView() {
val layoutManager = LinearLayoutManager(activity)
rvActions = rv_actions
rvActions.layoutManager = LinearLayoutManager(activity)
val adapter = RvActionsAdapter(cardContext) { position, destinationId -> navigate(destinationId as Int) }
adapter.setItems(mutableListOf(
NavigateAction(Action.Scan(), R.id.nav_scan),
NavigateAction(Action.Sign(), R.id.nav_sign)
))
rvActions.adapter = adapter
rvActions.layoutManager = layoutManager
rvActions.addItemDecoration(DividerItemDecoration(activity, layoutManager.orientation))
rvActions.adapter = RvActionsAdapter { type, position, data -> navigate(data.destinationId) }
}
override fun onResume() {
super.onResume()
val adapter: RvActionsAdapter = rvActions.adapter as? RvActionsAdapter ?: return
rvActions.adapter?.notifyDataSetChanged()
adapter.setItemList(getNavigateOptions())
adapter.notifyDataSetChanged()
}
private fun navigate(destinationId: Int) {
navController.navigate(destinationId, null, getDefaultNavigationOptions())
}
private fun getNavigateOptions(): MutableList<NavigateOptions> {
return mutableListOf(
NavigateOptions(Action.Scan, R.id.action_nav_entry_point_to_nav_scan),
NavigateOptions(Action.Sign, R.id.action_nav_entry_point_to_nav_sign),
NavigateOptions(Action.CreateWallet, R.id.action_nav_entry_point_to_nav_wallet_create),
NavigateOptions(Action.PurgeWallet, R.id.action_nav_entry_point_to_nav_wallet_purge),
NavigateOptions(Action.ReadIssuerData, R.id.action_nav_entry_point_to_nav_issuer_read_data),
NavigateOptions(Action.WriteIssuerData, R.id.action_nav_entry_point_to_nav_issuer_write_data),
NavigateOptions(Action.ReadIssuerExData, R.id.action_nav_entry_point_to_nav_issuer_read_ex_data),
NavigateOptions(Action.WriteIssuerExData, R.id.action_nav_entry_point_to_nav_issuer_write_ex_data),
NavigateOptions(Action.ReadUserData, R.id.action_nav_entry_point_to_nav_user_read_data),
NavigateOptions(Action.WriteUserData, R.id.action_nav_entry_point_to_nav_user_write_data)
)
}
}

View file

@ -1,69 +1,30 @@
package com.tangem.tangemtest._main.entry_point
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.CardContext
import com.tangem.tangemtest.card_use_cases.actions.Action
import com.tangem.tangemtest.commons.Bindable
import com.tangem.tangemtest.commons.NavigateAction
import com.tangem.tangemtest.commons.NavigateOptions
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvAdapter
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvCallback
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvVH
/**
[REDACTED_AUTHOR]
*/
typealias RvCallback = (Int, Any?) -> Unit
class RvActionsAdapter(callback: RvCallback<NavigateOptions>) : RvAdapter<RvActionsVH, NavigateOptions>(callback) {
class RvActionsAdapter(
private val cardContext: CardContext,
private val callback: RvCallback
) : RecyclerView.Adapter<RvActionsVH>() {
private val actionList = mutableListOf<NavigateAction>()
fun setItems(list: MutableList<NavigateAction>) {
actionList.clear()
actionList.addAll(list)
override fun createViewHolder(view: View, listener: RvCallback<NavigateOptions>?): RvActionsVH {
return RvActionsVH(view, listener)
}
fun addToItems(list: MutableList<NavigateAction>) {
actionList.addAll(list)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RvActionsVH {
val view = LayoutInflater.from(parent.context).inflate(R.layout.vh_actions, parent, false)
return RvActionsVH(view, cardContext.isVerified, callback)
}
override fun getItemCount(): Int = actionList.size
override fun onBindViewHolder(holder: RvActionsVH, position: Int) {
holder.bind(actionList[position])
}
override fun getLayoutId(): Int = R.layout.vh_actions
}
class RvActionsVH(
view: View,
private val cardIsVerified: Boolean,
private val callback: RvCallback
) : RecyclerView.ViewHolder(view), Bindable<NavigateAction> {
class RvActionsVH(itemView: View, callback: RvCallback<NavigateOptions>?) : RvVH<NavigateOptions>(itemView, callback) {
private val tvAction = itemView.findViewById<TextView>(R.id.tv_title)
private val navigateRight = itemView.findViewById<TextView>(R.id.tv_navigate_right)
override fun bind(data: NavigateAction) {
tvAction.text = tvAction.context.getString(data.action.resId)
itemView.setOnClickListener { callback(adapterPosition, data.destinationId) }
switchItemBackground(data.action)
}
private fun switchItemBackground(action: Action) {
when (action) {
is Action.Scan -> {
}
else -> itemView.visibility = if (cardIsVerified) View.VISIBLE else View.GONE
}
override fun onDataBound(data: NavigateOptions) {
tvAction.text = tvAction.context.getString(data.name.resName)
itemView.setOnClickListener { invokeCallback() }
}
}

View file

@ -1,41 +0,0 @@
package com.tangem.tangemtest.card_use_cases.actions
import android.os.Bundle
import android.view.View
import com.google.gson.Gson
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.base.BaseCardActionFragment
import com.tangem.tangemtest.commons.postUI
import com.tangem.tasks.ScanEvent
import com.tangem.tasks.TaskEvent
import kotlinx.android.synthetic.main.fg_card_scan.*
/**
[REDACTED_AUTHOR]
*/
class ScanActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_card_scan
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cardContext.reset()
cardContext.cardManager.scanCard { taskEvent ->
when (taskEvent) {
is TaskEvent.Event -> handleDataEvent(taskEvent.data)
is TaskEvent.Completion -> handleCompletionEvent(taskEvent)
}
}
}
private fun handleDataEvent(event: ScanEvent) {
when (event) {
is ScanEvent.OnReadEvent -> {
cardContext.card = event.card
postUI { tv_card_cid.text = Gson().toJson(cardContext.card) }
}
is ScanEvent.OnVerifyEvent -> cardContext.isVerified = true
}
}
}

View file

@ -1,40 +0,0 @@
package com.tangem.tangemtest.card_use_cases.actions
import android.os.Bundle
import android.view.View
import com.tangem.commands.SignResponse
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.base.BaseCardActionFragment
import com.tangem.tangemtest.commons.postUI
import com.tangem.tasks.TaskEvent
import kotlinx.android.synthetic.main.fg_card_sign.*
/**
[REDACTED_AUTHOR]
*/
class SignActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_card_sign
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val cardId = cardContext.requireCard().cardId
cardContext.cardManager.sign(createSampleHashes(), cardId) { taskEvent ->
when (taskEvent) {
is TaskEvent.Event -> handleDataEvent(taskEvent.data)
is TaskEvent.Completion -> handleCompletionEvent(taskEvent)
}
}
}
private fun handleDataEvent(event: SignResponse) {
postUI { tv_sign_result.text = toJson(event) }
}
private fun createSampleHashes(): Array<ByteArray> {
val hash1 = ByteArray(32) { 1 }
val hash2 = ByteArray(32) { 2 }
return arrayOf(hash1, hash2)
}
}

View file

@ -1,12 +0,0 @@
package com.tangem.tangemtest.card_use_cases.actions
import androidx.annotation.StringRes
import com.tangem.tangemtest.R
/**
[REDACTED_AUTHOR]
*/
sealed class Action(@StringRes val resId: Int) {
class Scan: Action(R.string.action_card_scan)
class Sign: Action(R.string.action_card_sign)
}

View file

@ -1,53 +0,0 @@
package com.tangem.tangemtest.card_use_cases.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.tangem.tangemtest.card_use_cases.CardContext
import com.tangem.tangemtest.commons.LayoutHolder
import com.tangem.tangemtest.commons.getDiManager
import com.tangem.tasks.TaskError
import com.tangem.tasks.TaskEvent
/**
[REDACTED_AUTHOR]
*/
abstract class BaseCardActionFragment : Fragment(), LayoutHolder {
protected lateinit var cardContext: CardContext
protected lateinit var mainView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mainView = inflater.inflate(getLayoutId(), container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cardContext = getDiManager().getCardContext()
}
@UiThread
protected fun handleCompletionEvent(taskEvent: TaskEvent.Completion<*>) {
val error: TaskError = taskEvent.error ?: return
when (error) {
is TaskError.UserCancelled -> showSnackbarMessage("Error: User was canceled")
else -> showSnackbarMessage("Description not implemented. code: ${error.code}")
}
}
protected fun showSnackbarMessage(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
protected fun toJson(obj: Any): String = Gson().toJson(obj)
}

View file

@ -1,4 +1,4 @@
package com.tangem.tangemtest.card_use_cases
package com.tangem.tangemtest.card_use_cases.models
import androidx.fragment.app.FragmentActivity
import com.tangem.CardManager

View file

@ -0,0 +1,64 @@
package com.tangem.tangemtest.card_use_cases.models.params.manager
import com.tangem.CardManager
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers.AfterActionModification
import com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers.AfterScanModifier
import com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers.ParamsChangeConsequence
import com.tangem.tasks.TaskEvent
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
data class AttrForAction(
val cardManager: CardManager,
val paramsList: List<IncomingParameter>,
val consequence: ParamsChangeConsequence?
)
interface CardAction {
fun executeMainAction(attrs: AttrForAction, callback: ActionCallback)
fun getActionByTag(tag: TlvTag, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
}
abstract class BaseCardAction : CardAction {
protected open fun handleResponse(
taskEvent: TaskEvent<*>,
modifier: AfterActionModification?,
attrs: AttrForAction,
callback: ActionCallback
) {
val allModifiedParams = mutableListOf<IncomingParameter>()
modifier?.modify(taskEvent, attrs.paramsList)?.forEach { parameter ->
allModifiedParams.add(parameter)
attrs.consequence?.affectChanges(parameter, attrs.paramsList)?.let { allModifiedParams.addAll(it) }
}
callback(taskEvent, allModifiedParams)
}
}
class ScanAction : BaseCardAction() {
override fun executeMainAction(attrs: AttrForAction, callback: ActionCallback) {
attrs.cardManager.scanCard { handleResponse(it, AfterScanModifier(), attrs, callback) }
}
}
class SignAction : BaseCardAction() {
override fun executeMainAction(attrs: AttrForAction, callback: ActionCallback) {
val hashes = findHashes()
val cardId = ParamsManager.findParameter(TlvTag.CardId, attrs.paramsList)?.data
attrs.cardManager.sign(hashes, stringOf(cardId)) { handleResponse(it, null, attrs, callback) }
}
override fun getActionByTag(tag: TlvTag, attrs: AttrForAction): ((ActionCallback) -> Unit)? {
if (tag != TlvTag.CardId) return null
val pCardId = ParamsManager.findParameter(tag, attrs.paramsList) ?: return null
return if (pCardId.data == null) { callback -> ScanAction().executeMainAction(attrs, callback) } else null
}
private fun findHashes(): Array<ByteArray> {
val hash1 = ByteArray(32) { 1 }
val hash2 = ByteArray(32) { 2 }
return arrayOf(hash1, hash2)
}
}

View file

@ -0,0 +1,19 @@
package com.tangem.tangemtest.card_use_cases.models.params.manager
import com.tangem.tangemtest.commons.Action
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class ParamsManagerFactory : BaseTypedHolder<Action, ParamsManager>() {
companion object {
fun createFactory(): ParamsManagerFactory {
return ParamsManagerFactory().apply {
register(Action.Scan, ScanParamsManager())
register(Action.Sign, SignParamsManager())
}
}
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.models.params.manager.IncomingParameter
import com.tangem.tangemtest.card_use_cases.models.params.manager.ParamsManager
import com.tangem.tasks.ScanEvent
import com.tangem.tasks.TaskEvent
interface AfterActionModification {
fun modify(taskEvent: TaskEvent<*>, paramsList: List<IncomingParameter>): List<IncomingParameter>
}
class AfterScanModifier : AfterActionModification {
override fun modify(taskEvent: TaskEvent<*>, paramsList: List<IncomingParameter>): List<IncomingParameter> {
val parameter = ParamsManager.findParameter(TlvTag.CardId, paramsList) ?: return listOf()
return if (taskEvent is TaskEvent.Event && taskEvent.data is ScanEvent.OnReadEvent) {
parameter.data = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
listOf(parameter)
} else listOf()
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.models.params.manager.IncomingParameter
/**
[REDACTED_AUTHOR]
*/
interface ParamsChangeConsequence {
fun affectChanges(changedParameter: IncomingParameter, paramsList: List<IncomingParameter>): List<IncomingParameter>
}
class ExampleConsequenceForCardId : ParamsChangeConsequence {
override fun affectChanges(changedParameter: IncomingParameter, paramsList: List<IncomingParameter>): List<IncomingParameter> {
if (changedParameter.tlvTag != TlvTag.CardId) return listOf()
val affectedList = paramsList.filter { it.tlvTag != changedParameter.tlvTag }
affectedList.forEach { it.data = changedParameter.data.hashCode() }
return affectedList
}
}

View file

@ -0,0 +1,87 @@
package com.tangem.tangemtest.card_use_cases.models.params.manager
import com.tangem.CardManager
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.models.params.manager.ParamsManager.Companion.findParameter
import com.tangem.tangemtest.card_use_cases.models.params.manager.modifiers.ParamsChangeConsequence
import com.tangem.tasks.TaskEvent
/**
[REDACTED_AUTHOR]
*/
class IncomingParameter(val tlvTag: TlvTag, var data: Any? = null)
typealias ActionResponse = TaskEvent<*>
typealias AffectedList = List<IncomingParameter>
typealias AffectedParamsCallback = (AffectedList) -> Unit
typealias ActionCallback = (ActionResponse, AffectedList) -> Unit
interface ParamsManager {
fun parameterChanged(tag: TlvTag, value: Any?, callback: AffectedParamsCallback? = null)
fun getParams(): List<IncomingParameter>
fun invokeMainAction(cardManager: CardManager, callback: ActionCallback)
fun getActionByTag(tag: TlvTag, cardManager: CardManager): ((ActionCallback) -> Unit)?
companion object {
fun findParameter(tlvTag: TlvTag, paramsList: List<IncomingParameter>)
: IncomingParameter? = paramsList.firstOrNull { it.tlvTag == tlvTag }
}
}
abstract class BaseParamsManager(protected val action: CardAction) : ParamsManager {
protected val paramsList: List<IncomingParameter> by lazy { createParamsList() }
abstract fun createParamsList(): List<IncomingParameter>
override fun parameterChanged(tag: TlvTag, value: Any?, callback: AffectedParamsCallback?) {
if (paramsList.isEmpty()) return
val foundParam = findParameter(tag, paramsList) ?: return
foundParam.data = value
applyChangesByAffectedParams(foundParam, callback)
}
override fun getParams(): MutableList<IncomingParameter> = paramsList.toMutableList()
// 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) }
}
protected open fun getChangeConsequence(): ParamsChangeConsequence? = null
protected fun getAttrsForAction(cardManager: CardManager)
: AttrForAction = AttrForAction(cardManager, paramsList, getChangeConsequence())
}
class ScanParamsManager : BaseParamsManager(ScanAction()) {
override fun createParamsList(): List<IncomingParameter> {
return listOf(IncomingParameter(TlvTag.CardId, null))
}
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()) {
override fun createParamsList(): List<IncomingParameter> {
return listOf(
IncomingParameter(TlvTag.CardId, null),
IncomingParameter(TlvTag.TransactionOutHash, "")
)
}
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
action.executeMainAction(getAttrsForAction(cardManager), callback)
}
override fun getActionByTag(tag: TlvTag, cardManager: CardManager): ((ActionCallback) -> Unit)? {
return action.getActionByTag(tag, getAttrsForAction(cardManager))
}
}

View file

@ -0,0 +1,104 @@
package com.tangem.tangemtest.card_use_cases.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.ui.widgets.ParameterWidget
import com.tangem.tangemtest.card_use_cases.view_models.ParamsViewModel
import com.tangem.tangemtest.commons.Action
import com.tangem.tangemtest.commons.LayoutHolder
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
abstract class BaseCardActionFragment : Fragment(), LayoutHolder {
protected lateinit var mainView: View
protected val incomingParamsContainer: ViewGroup by lazy {
mainView.findViewById<LinearLayout>(R.id.ll_incoming_params_container)
}
protected val viewModel: ParamsViewModel by viewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(this, "onCreateView")
mainView = inflater.inflate(getLayoutId(), container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(this, "onViewCreated")
val activity = activity ?: return
try {
viewModel.init(activity, getAction())
initFab()
val widgetList = createWidgets()
subscribeToViewModelChanges(widgetList)
} catch (e: Exception) {
Log.e(this, e)
showSnackbarMessage(e.toString())
}
}
private fun initFab() {
Log.d(this, "initFab")
val fab = mainView.findViewById<ExtendedFloatingActionButton>(R.id.fab_action) ?: return
fab.setText(getAction().resName)
fab.setOnClickListener { viewModel.invokeMainAction() }
}
private fun createWidgets(): MutableList<ParameterWidget> {
Log.d(this, "createWidgets")
val widgetList = mutableListOf<ParameterWidget>()
viewModel.getInitialParams()?.forEach { param ->
val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param)
widget.onParameterValueChanged = { viewModel.userChangedParameter(it) }
widget.onParameterAction = viewModel.getParameterAction(param.tlvTag)
widgetList.add(widget)
}
return widgetList
}
private fun subscribeToViewModelChanges(widgetList: MutableList<ParameterWidget>) {
Log.d(this, "subscribeToViewModelChanges")
viewModel.ldResponse.observe(viewLifecycleOwner, Observer {
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
mainView.findViewById<TextView>(R.id.tv_action_response_json).text = it
})
viewModel.ldError.observe(viewLifecycleOwner, Observer { showSnackbarMessage(it) })
viewModel.ldIncomingParameter.observe(viewLifecycleOwner, Observer { param ->
Log.d(this, "parameter changed from VM - name: ${param.tlvTag.name}, value:${param.data}")
widgetList.firstOrNull { it.tlvTag == param.tlvTag }?.changeParamValue(param.data)
})
}
protected fun showSnackbarMessage(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
private fun inflateParamView(where: ViewGroup): View {
val inflater = LayoutInflater.from(where.context)
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)
where.addView(view)
inflater.inflate(R.layout.m_divider_h, where, true)
return view
}
abstract fun getAction(): Action
}

View file

@ -0,0 +1,14 @@
package com.tangem.tangemtest.card_use_cases.ui
import com.tangem.tangemtest.R
import com.tangem.tangemtest.commons.Action
/**
[REDACTED_AUTHOR]
*/
class ScanActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_action_card_scan
override fun getAction(): Action = Action.Scan
}

View file

@ -0,0 +1,14 @@
package com.tangem.tangemtest.card_use_cases.ui
import com.tangem.tangemtest.R
import com.tangem.tangemtest.commons.Action
/**
[REDACTED_AUTHOR]
*/
class SignActionFragment : BaseCardActionFragment() {
override fun getLayoutId(): Int = R.layout.fg_action_card_sign
override fun getAction(): Action = Action.Sign
}

View file

@ -0,0 +1,88 @@
package com.tangem.tangemtest.card_use_cases.ui.widgets
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.models.params.manager.IncomingParameter
import com.tangem.tangemtest.commons.Action
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class ParameterWidget(parent: View, private val model: IncomingParameter) {
private val tvName: TextView = parent.findViewById(R.id.tv_param_name)
private val etValue: EditText = parent.findViewById(R.id.tv_param_value)
private val btnAction: Button = parent.findViewById(R.id.btn_action)
private val valueWatcher: TextWatcher by lazy { getWatcher() }
val tlvTag: TlvTag = model.tlvTag
private var parameterActionStorage: (() -> Unit)? = null
var onParameterAction: (() -> Unit)? = null
set(value) {
TransitionManager.beginDelayedTransition(etValue.parent as ViewGroup, AutoTransition())
if (value != null) {
parameterActionStorage = value
setActionButtonTitle()
btnAction.visibility = View.VISIBLE
} else {
btnAction.visibility = View.GONE
}
field = value
}
var onParameterValueChanged: ((IncomingParameter) -> Unit)? = null
init {
tvName.text = model.tlvTag.name
etValue.addTextChangedListener(valueWatcher)
btnAction.setOnClickListener { onParameterAction?.invoke() }
}
private fun setActionButtonTitle() {
val action = when (model.tlvTag) {
TlvTag.CardId -> Action.Scan
else -> Action.Unknown
}
btnAction.setText(action.resName)
}
private fun getWatcher(): TextWatcher {
return object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
val newValue = stringOf(s)
onParameterAction = if (newValue.isEmpty() && onParameterAction == null) parameterActionStorage
else null
model.data = newValue
onParameterValueChanged?.invoke(model)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
}
}
fun changeParamValue(data: Any?, silent: Boolean = true) {
Log.d(this, "changeParamValue: tag: $tlvTag, value: $data")
if (silent) {
etValue.removeTextChangedListener(valueWatcher)
etValue.setText(stringOf(data))
etValue.addTextChangedListener(valueWatcher)
} else {
etValue.setText(stringOf(data))
}
onParameterAction = null
}
}

View file

@ -0,0 +1,145 @@
package com.tangem.tangemtest.card_use_cases.view_models
import androidx.annotation.UiThread
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.gson.GsonBuilder
import com.tangem.common.tlv.TlvTag
import com.tangem.tangemtest.card_use_cases.models.CardContext
import com.tangem.tangemtest.card_use_cases.models.params.manager.IncomingParameter
import com.tangem.tangemtest.card_use_cases.models.params.manager.ParamsManager
import com.tangem.tangemtest.card_use_cases.models.params.manager.ParamsManagerFactory
import com.tangem.tangemtest.commons.Action
import com.tangem.tangemtest.commons.performAction
import com.tangem.tangemtest.commons.postUI
import com.tangem.tasks.ScanEvent
import com.tangem.tasks.TaskError
import com.tangem.tasks.TaskEvent
/**
[REDACTED_AUTHOR]
*/
class ParamsViewModel : ViewModel() {
val ldResponse: MutableLiveData<String> = MutableLiveData()
val ldError: MutableLiveData<String> = MutableLiveData()
val ldIncomingParameter: MutableLiveData<IncomingParameter> = MutableLiveData()
lateinit var ldCardContext: MutableLiveData<CardContext>
private set
private lateinit var ldParamsManager: LiveData<ParamsManager>
private lateinit var notifier: Notifier
// call before any subscriptions
fun init(context: FragmentActivity, action: Action) {
if (::ldCardContext.isInitialized && ::ldParamsManager.isInitialized) return
val factory = ParamsManagerFactory.createFactory()
val manager = factory.get(action) ?: throw NotImplementedError(
"ParamsManager for the ${context.getString(action.resName)} not implemented yet")
ldParamsManager = MutableLiveData(manager)
ldCardContext = MutableLiveData(CardContext().init(context))
notifier = Notifier(this)
}
fun getInitialParams(): List<IncomingParameter>? {
return ldParamsManager.value?.getParams()
}
fun userChangedParameter(param: IncomingParameter) {
parameterChanged(param.tlvTag, param.data)
}
private fun parameterChanged(tlvTag: TlvTag, value: Any?) {
ldParamsManager.value?.parameterChanged(tlvTag, value) { notifier.notifyParameterChanges(it) }
}
//invokes Scan, Sign etc...
fun invokeMainAction() {
performAction(ldParamsManager.value, ldCardContext.value?.cardManager) { paramsManager, cardManager ->
paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams ->
notifier.handleActionResult(response, listOfChangedParams)
}
}
}
fun getParameterAction(tag: TlvTag): (() -> Unit)? {
val paramsManager = ldParamsManager.value ?: return null
val cardManager = ldCardContext.value?.cardManager ?: return null
val parameterFunction = paramsManager.getActionByTag(tag, cardManager) ?: return null
return {
parameterFunction { response, listOfChangedParams ->
notifier.handleActionResult(response, listOfChangedParams)
}
}
}
}
internal class Notifier(private val vm: ParamsViewModel) {
private val gsonConverter = GsonBuilder().setPrettyPrinting().create()
private var notShowedError: TaskError? = null
fun handleActionResult(response: TaskEvent<*>, list: List<IncomingParameter>) {
postUI { notifyParameterChanges(list) }
handleResponse(response)
}
@UiThread
fun notifyParameterChanges(list: List<IncomingParameter>) {
list.forEach { vm.ldIncomingParameter.value = it }
}
fun handleResponse(response: TaskEvent<*>) {
val taskEvent = response as? TaskEvent<*> ?: return
postUI {
when (taskEvent) {
is TaskEvent.Completion -> handleCompletionEvent(taskEvent)
is TaskEvent.Event -> handleDataEvent(taskEvent.data)
}
}
}
@UiThread
private fun handleDataEvent(event: Any?) {
when (event) {
is ScanEvent.OnReadEvent -> {
val cardContext = vm.ldCardContext.value ?: return
cardContext.card = event.card
vm.ldCardContext.value = cardContext
vm.ldResponse.value = gsonConverter.toJson(event)
}
is ScanEvent.OnVerifyEvent -> {
val cardContext = vm.ldCardContext.value ?: return
cardContext.isVerified = true
vm.ldCardContext.value = cardContext
}
else -> vm.ldResponse.value = gsonConverter.toJson(event)
}
}
@UiThread
private fun handleCompletionEvent(taskEvent: TaskEvent.Completion<*>) {
val error: TaskError = taskEvent.error ?: return
when (error) {
is TaskError.UserCancelled -> {
if (notShowedError == null) {
vm.ldError.value = "User was cancelled"
} else {
vm.ldError.value = "Error description not implemented. Code: ${notShowedError!!.code}"
notShowedError = null
}
}
else -> notShowedError = error
}
}
}

View file

@ -5,8 +5,4 @@ package com.tangem.tangemtest.commons
*/
interface LayoutHolder {
fun getLayoutId(): Int
}
interface Bindable<T> {
fun bind(data: T)
}

View file

@ -1,17 +0,0 @@
package com.tangem.tangemtest.commons
import android.content.Context
import androidx.fragment.app.Fragment
import com.tangem.tangemtest.card_use_cases.CardContext
/**
[REDACTED_AUTHOR]
*/
interface DiManager {
fun getCardContext(): CardContext
}
fun Context.getDiManager(): DiManager = applicationContext as DiManager
fun Fragment.getDiManager(): DiManager = activity?.applicationContext as DiManager

View file

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

View file

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

View file

@ -1,9 +1,9 @@
package com.tangem.tangemtest.commons
import androidx.annotation.StringRes
import androidx.navigation.NavOptions
import androidx.navigation.navOptions
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.actions.Action
/**
[REDACTED_AUTHOR]
@ -20,4 +20,18 @@ fun getDefaultNavigationOptions(): NavOptions {
}
}
data class NavigateAction(val action: Action, val destinationId: Int)
data class NavigateOptions(val name: Action, val destinationId: Int)
enum class Action(@StringRes val resName: Int) {
Scan(R.string.action_card_scan),
Sign(R.string.action_card_sign),
CreateWallet(R.string.action_wallet_create),
PurgeWallet(R.string.action_wallet_purge),
ReadIssuerData(R.string.action_issuer_read_data),
WriteIssuerData(R.string.action_issuer_write_data),
ReadIssuerExData(R.string.action_issuer_read_ex_data),
WriteIssuerExData(R.string.action_issuer_write_ex_data),
ReadUserData(R.string.action_user_read_data),
WriteUserData(R.string.action_user_write_data),
Unknown(R.string.unknown),
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
</selector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/primary_text_dark" android:state_enabled="true"/>
<item android:alpha="0.38" android:color="?attr/colorOnSurface"/>
</selector>

View file

@ -93,7 +93,7 @@
<include
android:id="@+id/m_delimiter_h"
layout="@layout/m_delimiter_h"
layout="@layout/m_divider_h"
android:layout_width="0dp"
android:layout_height="@dimen/delimiter_size"
android:layout_marginTop="16dp"

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_incoming_params_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
<include layout="@layout/w_action_response_json" />
</LinearLayout>
</ScrollView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/def_indent"
android:layout_marginBottom="@dimen/def_double_indent"
android:text="any action" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_incoming_params_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
<include layout="@layout/w_action_response_json" />
</LinearLayout>
</ScrollView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/def_indent"
android:layout_marginBottom="@dimen/def_double_indent"
android:text="any action" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,17 +0,0 @@
<?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_card_cid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_card_scan"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,17 +0,0 @@
<?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_sign_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_card_sign"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/m_delimiter_h"
android:layout_width="match_parent"
android:layout_height="@dimen/delimiter_size"
android:background="@color/delimiter"
android:orientation="vertical" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/m_delimiter_h"
android:layout_width="match_parent"
android:layout_height="@dimen/delimiter_size"
android:background="@color/delimiter" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/m_delimiter_h"
android:layout_width="@dimen/delimiter_size"
android:layout_height="match_parent"
android:background="@color/delimiter" />

View file

@ -2,30 +2,20 @@
<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:layout_height="48dp"
xmlns:tools="http://schemas.android.com/tools"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_navigate_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
android:text=">"
android:layout_marginStart="@dimen/def_indent"
android:layout_marginEnd="@dimen/def_indent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Action method" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

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

View file

@ -0,0 +1,60 @@
<?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">
<TextView
android:id="@+id/tv_param_name"
style="@style/TvParamNameStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/include4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Param name" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.37" />
<include
android:id="@+id/include4"
layout="@layout/m_divider_h"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<EditText
android:id="@+id/tv_param_value"
style="@style/EtParamValueStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/tv_param_name"
app:layout_constraintEnd_toStartOf="@+id/btn_action"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="@+id/tv_param_name"
tools:text="Param value" />
<Button
android:id="@+id/btn_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/def_indent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Action"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -8,51 +8,99 @@
android:id="@+id/nav_entry_point"
android:name="com.tangem.tangemtest._main.entry_point.EntryPointFragment"
android:label="@string/fg_name_entry_point"
tools:layout="@layout/fg_entry_point" />
tools:layout="@layout/fg_entry_point">
<action
android:id="@+id/action_nav_entry_point_to_nav_issuer_read_ex_data"
app:destination="@id/nav_issuer_read_ex_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_issuer_read_data"
app:destination="@id/nav_issuer_read_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_issuer_write_data"
app:destination="@id/nav_issuer_write_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_issuer_write_ex_data"
app:destination="@id/nav_issuer_write_ex_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_scan"
app:destination="@id/nav_scan" />
<action
android:id="@+id/action_nav_entry_point_to_nav_sign"
app:destination="@id/nav_sign" />
<action
android:id="@+id/action_nav_entry_point_to_nav_user_read_data"
app:destination="@id/nav_user_read_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_user_write_data"
app:destination="@id/nav_user_write_data" />
<action
android:id="@+id/action_nav_entry_point_to_nav_wallet_create"
app:destination="@id/nav_wallet_create" />
<action
android:id="@+id/action_nav_entry_point_to_nav_wallet_purge"
app:destination="@id/nav_wallet_purge" />
</fragment>
<fragment
android:id="@+id/nav_scan"
android:name="com.tangem.tangemtest.card_use_cases.actions.ScanActionFragment"
android:name="com.tangem.tangemtest.card_use_cases.ui.ScanActionFragment"
android:label="@string/action_card_scan"
tools:layout="@layout/fg_card_scan" />
tools:layout="@layout/fg_action_card_scan" />
<fragment
android:id="@+id/nav_sign"
android:name="com.tangem.tangemtest.card_use_cases.actions.SignActionFragment"
android:label="@string/action_card_scan"
tools:layout="@layout/fg_card_sign" />
android:name="com.tangem.tangemtest.card_use_cases.ui.SignActionFragment"
android:label="@string/action_card_sign"
tools:layout="@layout/fg_action_card_sign" />
<!-- <fragment-->
<!-- android:id="@+id/flow_step_one_dest"-->
<!-- android:name="com.example.android.codelabs.navigation.FlowStepFragment"-->
<!-- tools:layout="@layout/flow_step_one_fragment">-->
<!-- <argument-->
<!-- android:name="flowStepNumber"-->
<!-- android:defaultValue="1"-->
<!-- app:argType="integer" />-->
<!-- <action-->
<!-- android:id="@+id/next_action"-->
<!-- app:destination="@+id/flow_step_two_dest" />-->
<!-- </fragment>-->
<fragment
android:id="@+id/nav_issuer_read_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.IssuerReadDataActionFragment"
android:label="@string/action_issuer_read_data"
tools:layout="@layout/fg_action_issuer_read_data" />
<!-- <fragment-->
<!-- android:id="@+id/settings_dest"-->
<!-- android:name="com.example.android.codelabs.navigation.SettingsFragment"-->
<!-- android:label="@string/settings"-->
<!-- tools:layout="@layout/settings_fragment" />-->
<fragment
android:id="@+id/nav_issuer_write_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.IssuerWriteDataActionFragment"
android:label="@string/action_issuer_write_data"
tools:layout="@layout/fg_action_issuer_write_data" />
<!-- <fragment-->
<!-- android:id="@+id/deeplink_dest"-->
<!-- android:name="com.example.android.codelabs.navigation.DeepLinkFragment"-->
<!-- android:label="@string/deeplink"-->
<!-- tools:layout="@layout/deeplink_fragment">-->
<fragment
android:id="@+id/nav_issuer_read_ex_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.IssuerReadExDataActionFragment"
android:label="@string/action_issuer_read_ex_data"
tools:layout="@layout/fg_action_issuer_read_ex_data" />
<!-- <argument-->
<!-- android:name="myarg"-->
<!-- android:defaultValue="Android!" />-->
<fragment
android:id="@+id/nav_issuer_write_ex_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.IssuerWriteExDataActionFragment"
android:label="@string/action_issuer_write_ex_data"
tools:layout="@layout/fg_action_issuer_write_ex_data" />
<!-- <deepLink app:uri="www.example.com/{myarg}" />-->
<fragment
android:id="@+id/nav_user_read_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.UserReadDataActionFragment"
android:label="@string/action_user_read_data"
tools:layout="@layout/fg_action_user_read_data" />
<fragment
android:id="@+id/nav_user_write_data"
android:name="com.tangem.tangemtest.card_use_cases.ui.UserWriteDataActionFragment"
android:label="@string/action_user_write_data"
tools:layout="@layout/fg_action_user_write_data" />
<fragment
android:id="@+id/nav_wallet_create"
android:name="com.tangem.tangemtest.card_use_cases.ui.WalletCreateActionFragment"
android:label="@string/action_wallet_create"
tools:layout="@layout/fg_action_wallet_create" />
<fragment
android:id="@+id/nav_wallet_purge"
android:name="com.tangem.tangemtest.card_use_cases.ui.WalletPurgeActionFragment"
android:label="@string/action_wallet_purge"
tools:layout="@layout/fg_action_wallet_purge" />
<!-- </fragment>-->
</navigation>

View file

@ -4,5 +4,5 @@
<color name="colorPrimaryDark">#027AFF</color>
<color name="colorAccent">#027AFF</color>
<color name="delimiter">#000000</color>
<color name="delimiter">#26000000</color>
</resources>

View file

@ -3,4 +3,13 @@
<dimen name="delimiter_size">1dp</dimen>
<dimen name="def_half_indent">8dp</dimen>
<dimen name="def_indent">16dp</dimen>
<dimen name="def_double_indent">32dp</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>
<dimen name="indent_card_incoming_param_value_h">@dimen/def_indent</dimen>
<dimen name="indent_card_incoming_param_value_v">0dp</dimen>
</resources>

View file

@ -2,6 +2,16 @@
<string name="app_name">Tangem Demo</string>
<string name="fg_name_entry_point">@string/app_name</string>
<string name="action_card_scan">Read card</string>
<string name="action_card_scan">Scan card</string>
<string name="action_card_sign">Sign card</string>
<string name="action_wallet_create">Create wallet</string>
<string name="action_wallet_purge">Purge wallet</string>
<string name="action_issuer_read_data">Read issuer data</string>
<string name="action_issuer_write_data">Write issuer data</string>
<string name="action_issuer_read_ex_data">Read issuer extra data</string>
<string name="action_issuer_write_ex_data">Write issuer extra data</string>
<string name="action_user_read_data">Read user data</string>
<string name="action_user_write_data">Write user data</string>
<string name="current_card_id">"Current card ID: "</string>
<string name="unknown">unknown</string>
</resources>

View file

@ -1,11 +1,26 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="TvParamNameStyle" parent="@style/Widget.AppCompat.TextView">
<item name="android:layout_marginStart">@dimen/indent_card_incoming_param_name_h</item>
<item name="android:layout_marginEnd">@dimen/indent_card_incoming_param_name_h</item>
<item name="android:layout_marginTop">@dimen/indent_card_incoming_param_name_v</item>
<item name="android:layout_marginBottom">@dimen/indent_card_incoming_param_name_v</item>
</style>
<style name="EtParamValueStyle" parent="@style/Widget.AppCompat.EditText">
<item name="android:layout_marginStart">@dimen/indent_card_incoming_param_value_h</item>
<item name="android:layout_marginEnd">@dimen/indent_card_incoming_param_value_h</item>
<item name="android:layout_marginTop">@dimen/indent_card_incoming_param_value_v</item>
<item name="android:layout_marginBottom">@dimen/indent_card_incoming_param_value_v</item>
</style>
</resources>