Updated on 2026-08-14
This commit is contained in:
parent
586dc1a563
commit
bd91231803
42 changed files with 963 additions and 343 deletions
|
|
@ -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'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,4 @@ package com.tangem.tangemtest.commons
|
|||
*/
|
||||
interface LayoutHolder {
|
||||
fun getLayoutId(): Int
|
||||
}
|
||||
|
||||
interface Bindable<T> {
|
||||
fun bind(data: T)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
@ -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),
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
40
tangem-demo/src/main/res/layout/fg_action_card_scan.xml
Normal file
40
tangem-demo/src/main/res/layout/fg_action_card_scan.xml
Normal 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>
|
||||
40
tangem-demo/src/main/res/layout/fg_action_card_sign.xml
Normal file
40
tangem-demo/src/main/res/layout/fg_action_card_sign.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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" />
|
||||
6
tangem-demo/src/main/res/layout/m_divider_h.xml
Normal file
6
tangem-demo/src/main/res/layout/m_divider_h.xml
Normal 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" />
|
||||
6
tangem-demo/src/main/res/layout/m_divider_v.xml
Normal file
6
tangem-demo/src/main/res/layout/m_divider_v.xml
Normal 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" />
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
60
tangem-demo/src/main/res/layout/w_card_incoming_param.xml
Normal file
60
tangem-demo/src/main/res/layout/w_card_incoming_param.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue