Updated on 2026-08-14

This commit is contained in:
Tangem 2020-03-10 16:39:20 +03:00
parent 5a452ba1e2
commit 586dc1a563
30 changed files with 721 additions and 173 deletions

View file

@ -3,8 +3,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.tangem.tangemtest"
@ -34,13 +33,22 @@ android {
dependencies {
implementation project(':tangem-core')
implementation project(':tangem-sdk')
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4"
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'
}

View file

@ -8,8 +8,8 @@
<uses-permission android:name="android.permission.NFC" />
<application
android:name=".AppTangemDemo"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -17,15 +17,15 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Old_MainActivity">
<activity android:name="._main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.tangem.com"
android:scheme="http" />

View file

@ -0,0 +1,14 @@
package com.tangem.tangemtest
import android.app.Application
import com.tangem.tangemtest.card_use_cases.CardContext
import com.tangem.tangemtest.commons.DiManager
/**
[REDACTED_AUTHOR]
*/
class AppTangemDemo : Application(), DiManager {
private val cardContext: CardContext = CardContext()
override fun getCardContext(): CardContext = cardContext
}

View file

@ -1,161 +0,0 @@
package com.tangem.tangemtest
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tangem.CardManager
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tasks.ScanEvent
import com.tangem.tasks.TaskError
import com.tangem.tasks.TaskEvent
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var cardManager: CardManager
private lateinit var cardId: String
private lateinit var issuerData: ByteArray
private lateinit var issuerDataSignature: ByteArray
private var issuerDataCounter: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cardManager = CardManager.init(this)
btn_scan?.setOnClickListener { _ ->
cardManager.scanCard { taskEvent ->
when (taskEvent) {
is TaskEvent.Event -> {
when (taskEvent.data) {
is ScanEvent.OnReadEvent -> {
// Handle returned card data
cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
runOnUiThread {
tv_card_cid?.text = cardId
btn_create_wallet.isEnabled = true
}
}
is ScanEvent.OnVerifyEvent -> {
//Handle card verification
runOnUiThread {
tv_card_cid?.text = cardId
btn_sign.isEnabled = true
btn_read_issuer_data.isEnabled = true
btn_read_issuer_extra_data.isEnabled = true
btn_write_issuer_data.isEnabled = true
btn_purge_wallet.isEnabled = true
btn_create_wallet.isEnabled = true
}
}
}
}
is TaskEvent.Completion -> {
if (taskEvent.error != null) {
if (taskEvent.error is TaskError.UserCancelled) {
// Handle case when user cancelled manually
}
// Handle other errors
}
// Handle completion
}
}
}
}
btn_sign?.setOnClickListener { _ ->
cardManager.sign(
createSampleHashes(),
cardId) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread { tv_card_cid?.text = cardId + "was used to sign sample hashes." }
}
}
}
btn_read_issuer_data?.setOnClickListener { _ ->
cardManager.readIssuerData(cardId) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread {
btn_write_issuer_data.isEnabled = true
tv_card_cid?.text = it.data.issuerData.contentToString()
issuerData = it.data.issuerData
issuerDataSignature = it.data.issuerDataSignature
}
}
}
}
btn_write_issuer_data?.setOnClickListener { _ ->
cardManager.writeIssuerData(
cardId,
issuerData,
issuerDataSignature) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread {
tv_card_cid?.text = it.data.cardId
}
}
}
}
btn_read_issuer_extra_data?.setOnClickListener { _ ->
cardManager.readIssuerExtraData(cardId) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread {
issuerDataCounter = (it.data.issuerDataCounter ?: 0) + 1
btn_write_issuer_data.isEnabled = true
tv_card_cid?.text = "Read ${it.data.issuerData.size} bytes of data."
}
}
}
}
btn_purge_wallet?.setOnClickListener { _ ->
cardManager.purgeWallet(
cardId) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread {
tv_card_cid?.text = it.data.status.name
}
}
}
}
btn_create_wallet?.setOnClickListener { _ ->
cardManager.createWallet(
cardId) {
when (it) {
is TaskEvent.Completion -> {
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
}
is TaskEvent.Event -> runOnUiThread {
tv_card_cid?.text = it.data.status.name
btn_sign.isEnabled = true
btn_read_issuer_data.isEnabled = true
btn_purge_wallet.isEnabled = true
btn_create_wallet.isEnabled = false
}
}
}
}
btn_read_write_user_data?.setOnClickListener { startActivity(Intent(this, TestUserDataActivity::class.java)) }
}
private fun createSampleHashes(): Array<ByteArray> {
val hash1 = ByteArray(32) { 1 }
val hash2 = ByteArray(32) { 2 }
return arrayOf(hash1, hash2)
}
}

View file

@ -0,0 +1,48 @@
package com.tangem.tangemtest._main
import android.content.res.Resources
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import com.tangem.tangemtest.R
/**
* A simple activity demonstrating use of a NavHostFragment with a navigation drawer.
*/
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.a_main)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_entry_point))
setupActionBarWithNavController(navController, appBarConfiguration)
navController.addOnDestinationChangedListener { _, destination, _ ->
val dest: String = try {
resources.getResourceName(destination.id)
} catch (e: Resources.NotFoundException) {
destination.id.toString()
}
Log.d("NavigationActivity", "Navigated to $dest")
}
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
}
}

View file

@ -0,0 +1,62 @@
package com.tangem.tangemtest._main.entry_point
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
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.getDefaultNavigationOptions
import com.tangem.tangemtest.commons.getDiManager
import kotlinx.android.synthetic.main.fg_entry_point.*
/**
[REDACTED_AUTHOR]
*/
class EntryPointFragment : Fragment() {
private val navController: NavController by lazy { findNavController() }
private lateinit var rvActions: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fg_entry_point, container, false)
}
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)
}
private fun initRecyclerView(cardContext: CardContext) {
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
}
override fun onResume() {
super.onResume()
rvActions.adapter?.notifyDataSetChanged()
}
private fun navigate(destinationId: Int) {
navController.navigate(destinationId, null, getDefaultNavigationOptions())
}
}

View file

@ -0,0 +1,69 @@
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
/**
[REDACTED_AUTHOR]
*/
typealias RvCallback = (Int, Any?) -> Unit
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)
}
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])
}
}
class RvActionsVH(
view: View,
private val cardIsVerified: Boolean,
private val callback: RvCallback
) : RecyclerView.ViewHolder(view), Bindable<NavigateAction> {
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
}
}
}

View file

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

View file

@ -0,0 +1,41 @@
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

@ -0,0 +1,40 @@
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

@ -0,0 +1,12 @@
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

@ -0,0 +1,53 @@
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

@ -0,0 +1,12 @@
package com.tangem.tangemtest.commons
/**
[REDACTED_AUTHOR]
*/
interface LayoutHolder {
fun getLayoutId(): Int
}
interface Bindable<T> {
fun bind(data: T)
}

View file

@ -0,0 +1,17 @@
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,23 @@
package com.tangem.tangemtest.commons
import androidx.navigation.NavOptions
import androidx.navigation.navOptions
import com.tangem.tangemtest.R
import com.tangem.tangemtest.card_use_cases.actions.Action
/**
[REDACTED_AUTHOR]
*/
fun getDefaultNavigationOptions(): NavOptions {
return navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
}
data class NavigateAction(val action: Action, val destinationId: Int)

View file

@ -0,0 +1,30 @@
package com.tangem.tangemtest.commons
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
/**
[REDACTED_AUTHOR]
*/
object Threading {
val uiHandler: Handler = Handler(Looper.getMainLooper())
val workHandler: Handler = Handler(HandlerThread("Application worker thread").apply { start() }.looper)
fun postInCurrentThread(msTime: Long, func: () -> Unit) {
Handler().postDelayed({ func() }, msTime)
}
}
fun post(msTime: Long, func: () -> Unit) {
Threading.postInCurrentThread(msTime, func)
}
fun postUI(msTime: Long = 0, func: () -> Unit) {
if (msTime > 0) Threading.uiHandler.postDelayed({ func() }, msTime) else Threading.uiHandler.post(func)
}
fun postWork(msTime: Long = 0, func: () -> Unit) {
if (msTime > 0) Threading.workHandler.postDelayed({ func() }, msTime) else Threading.workHandler.post(func)
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="-100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="-100%"
android:toYDelta="0%" />
</set>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="100%"
android:toYDelta="0%" />
</set>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation" />
</LinearLayout>

View file

@ -0,0 +1,17 @@
<?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

@ -0,0 +1,17 @@
<?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

@ -0,0 +1,16 @@
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_actions"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
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=">"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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"
app:startDestination="@+id/nav_entry_point">
<fragment
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" />
<fragment
android:id="@+id/nav_scan"
android:name="com.tangem.tangemtest.card_use_cases.actions.ScanActionFragment"
android:label="@string/action_card_scan"
tools:layout="@layout/fg_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" />
<!-- <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/settings_dest"-->
<!-- android:name="com.example.android.codelabs.navigation.SettingsFragment"-->
<!-- android:label="@string/settings"-->
<!-- tools:layout="@layout/settings_fragment" />-->
<!-- <fragment-->
<!-- android:id="@+id/deeplink_dest"-->
<!-- android:name="com.example.android.codelabs.navigation.DeepLinkFragment"-->
<!-- android:label="@string/deeplink"-->
<!-- tools:layout="@layout/deeplink_fragment">-->
<!-- <argument-->
<!-- android:name="myarg"-->
<!-- android:defaultValue="Android!" />-->
<!-- <deepLink app:uri="www.example.com/{myarg}" />-->
<!-- </fragment>-->
</navigation>

View file

@ -1,3 +1,7 @@
<resources>
<string name="app_name">TangemTest</string>
<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_sign">Sign card</string>
</resources>

View file

@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>