Skip to content

Commit

Permalink
Merge branch 'develop' into bugfix/AND-4628_move_nfc_enabled_check
Browse files Browse the repository at this point in the history
# Conflicts:
#	tangem-sdk-android/src/main/java/com/tangem/sdk/DefaultSessionViewDelegate.kt
  • Loading branch information
Sateetas committed Nov 2, 2023
2 parents 1c36afa + 7a73152 commit a015dec
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 31 deletions.
6 changes: 3 additions & 3 deletions tangem-sdk-android-demo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ dependencies {
implementation project(':tangem-sdk-core')
implementation project(':tangem-sdk-android')

testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
implementation 'androidx.core:core-ktx:1.3.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@ class SdkTaskSpinnerFragment : BaseFragment() {

class EmptyViewDelegate : SessionViewDelegate {
override val resetCodesViewDelegate: ResetCodesViewDelegate = EmptyResetCodesViewDelegate()
override fun onSessionStarted(cardId: String?, message: ViewDelegateMessage?, enableHowTo: Boolean) {}
override fun onSessionStarted(
cardId: String?,
message: ViewDelegateMessage?,
enableHowTo: Boolean,
iconScanRes: Int?,
) {}
override fun onSecurityDelay(ms: Int, totalDurationSeconds: Int) {}
override fun onDelay(total: Int, current: Int, step: Int) {}
override fun onTagLost() {}
Expand Down
6 changes: 3 additions & 3 deletions tangem-sdk-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ dependencies {
implementation 'at.favre.lib:armadillo:1.0.0'

// testing
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ class DefaultSessionViewDelegate(
private var nfcEnableDialog: NfcEnableDialog? = null
private var stoppedBySession: Boolean = false

override fun onSessionStarted(cardId: String?, message: ViewDelegateMessage?, enableHowTo: Boolean) {
override fun onSessionStarted(
cardId: String?,
message: ViewDelegateMessage?,
enableHowTo: Boolean,
iconScanRes: Int?,
) {
Log.view { "session started" }
createAndShowState(SessionViewDelegateState.Ready(formatCardId(cardId)), enableHowTo, message)
createAndShowState(SessionViewDelegateState.Ready(formatCardId(cardId)), enableHowTo, message, iconScanRes)
checkNfcEnabled()
}

Expand Down Expand Up @@ -158,10 +163,11 @@ class DefaultSessionViewDelegate(
state: SessionViewDelegateState,
enableHowTo: Boolean,
message: ViewDelegateMessage? = null,
iconScanRes: Int? = null,
) {
postUI {
if (readingDialog == null) {
createReadingDialog(activity)
createReadingDialog(activity, iconScanRes)
} else {
readingDialog?.dismissInternal()
}
Expand All @@ -172,12 +178,13 @@ class DefaultSessionViewDelegate(
}
}

private fun createReadingDialog(activity: Activity) {
private fun createReadingDialog(activity: Activity, iconScanRes: Int? = null) {
val nfcLocationProvider = NfcAntennaLocationProvider(Build.DEVICE)
readingDialog = NfcSessionDialog(
context = activity.sdkThemeContext(),
nfcManager = nfcManager,
nfcLocationProvider = nfcLocationProvider,
iconScanRes = iconScanRes,
).apply {
setOwnerActivity(activity)
dismissWithAnimation = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,18 @@ internal class AndroidKeystoreManager(
return try {
RSACipherOperations.initUnwrapKeyCipher(masterPrivateKey)
} catch (e: UserNotAuthenticatedException) {
Log.warning { "$TAG - Unable to initialize the cipher because the user is not authenticated" }

authenticationManager.authenticate()

initUnwrapCipherAfterAuthentication()
authenticateAndInitUnwrapCipher()
} catch (e: InvalidKeyException) {
handleInvalidKeyException(e)
}
}

private fun initUnwrapCipherAfterAuthentication(): Cipher {
Log.debug { "$TAG - Reinitializing the unwrap cipher" }
private suspend fun authenticateAndInitUnwrapCipher(): Cipher {
Log.warning { "$TAG - Unable to initialize the cipher because the user is not authenticated" }

return try {
authenticationManager.authenticate()

RSACipherOperations.initUnwrapKeyCipher(masterPrivateKey)
} catch (e: InvalidKeyException) {
handleInvalidKeyException(e)
Expand Down Expand Up @@ -157,7 +157,6 @@ internal class AndroidKeystoreManager(
.let { builder ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
builder
.setUnlockedDeviceRequired(true)
.setUserAuthenticationParameters(
MASTER_KEY_TIMEOUT_SECONDS,
KeyProperties.AUTH_BIOMETRIC_STRONG,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class NfcSessionDialog(
context: Context,
private val nfcManager: NfcManager,
private val nfcLocationProvider: NfcLocationProvider,
private val iconScanRes: Int? = null,
) : BaseSdkDialog(context) {

private lateinit var taskContainer: ViewGroup
Expand Down Expand Up @@ -67,6 +68,7 @@ class NfcSessionDialog(

headerWidget = HeaderWidget(view.findViewById(R.id.llHeader))
touchCardWidget = TouchCardWidget(view.findViewById(R.id.flImageContainer), nfcLocation)
touchCardWidget.setIconScanRes(iconScanRes)
progressStateWidget = ProgressbarStateWidget(view.findViewById(R.id.clProgress))
pinCodeRequestWidget = PinCodeRequestWidget(view.findViewById(R.id.csPinCode))
pinCodeSetChangeWidget = PinCodeModificationWidget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ class TouchCardWidget(
customBitmapHolder?.bitmap?.recycle()
}

fun setIconScanRes(iconScanRes: Int?) {
if (iconScanRes != null) {
ivHandCardHorizontal.setImageResource(iconScanRes)
ivHandCardVertical.setImageResource(iconScanRes)
} else {
ivHandCardHorizontal.setImageResource(R.drawable.hand_full_card_horizontal)
ivHandCardVertical.setImageResource(R.drawable.hand_full_card_vertical)
}
}

private data class BitmapImageHolder(
val bitmapArray: ByteArray,
val bitmap: Bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.size, BitmapFactory.Options()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ interface SessionViewDelegate {
/**
* It is called when user is expected to scan a Tangem Card with an Android device.
*/
fun onSessionStarted(cardId: String?, message: ViewDelegateMessage? = null, enableHowTo: Boolean)
fun onSessionStarted(
cardId: String?,
message: ViewDelegateMessage? = null,
enableHowTo: Boolean,
iconScanRes: Int? = null,
)

/**
* It is called when security delay is triggered by the card.
Expand Down
13 changes: 10 additions & 3 deletions tangem-sdk-core/src/main/java/com/tangem/TangemSdk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,7 @@ class TangemSdk(
cardId: String? = null,
initialMessage: Message? = null,
accessCode: String? = null,
iconScanRes: Int? = null,
callback: CompletionCallback<T>,
) {
if (checkSession()) {
Expand All @@ -1030,7 +1031,13 @@ class TangemSdk(

configure()
cardSession = makeSession(cardId, initialMessage, accessCode)
Thread().run { cardSession?.startWithRunnable(runnable, callback) }
Thread().run {
cardSession?.startWithRunnable(
iconScanRes = iconScanRes,
runnable = runnable,
callback = callback,
)
}
}

/**
Expand Down Expand Up @@ -1103,15 +1110,15 @@ class TangemSdk(
val jsonrpcLinker = linkersList[0]
cardSession = makeSession(cardId, message, accessCode)
Thread().run {
cardSession?.startWithRunnable(jsonrpcLinker.runnable!!) {
cardSession?.startWithRunnable(runnable = jsonrpcLinker.runnable!!) {
jsonrpcLinker.linkResult(it)
callback(jsonrpcLinker.response.toJson())
}
}
} else {
val task = RunnablesTask(linkersList)
cardSession = makeSession(cardId, message, accessCode)
cardSession!!.startWithRunnable(task) { result ->
cardSession!!.startWithRunnable(runnable = task) { result ->
when (result) {
is CompletionResult.Success -> callback(converter.toJson(result.data.responses))
is CompletionResult.Failure -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,15 @@ class CardSession(
/**
* This method starts a card session, performs preflight [ReadCommand],
* invokes [CardSessionRunnable.run] and closes the session.
* @param iconScanRes iconResource to replace on Scan Bottom Sheet
* @param runnable [CardSessionRunnable] that will be performed in the session.
* @param callback will be triggered with a [CompletionResult] of a session.
*/
fun <T : CardSessionRunnable<R>, R> startWithRunnable(runnable: T, callback: CompletionCallback<R>) {
fun <T : CardSessionRunnable<R>, R> startWithRunnable(
iconScanRes: Int? = null,
runnable: T,
callback: CompletionCallback<R>,
) {
if (state != CardSessionState.Inactive) {
val error = TangemSdkError.Busy()
Log.error { "$error" }
Expand All @@ -124,7 +129,7 @@ class CardSession(
prepareSession(runnable) { prepareResult ->
when (prepareResult) {
is CompletionResult.Success -> {
start { _, error ->
start(iconScanRes) { _, error ->
if (error != null) {
Log.error { "$error" }
callback(CompletionResult.Failure(error))
Expand Down Expand Up @@ -156,12 +161,13 @@ class CardSession(

/**
* Starts a card session and performs preflight [ReadCommand].
* @param iconScanRes iconResource to replace on Scan Bottom Sheet
* @param onSessionStarted: callback with the card session. Can contain [TangemSdkError] if something goes wrong.
*/
fun start(onSessionStarted: SessionStartedCallback) {
fun start(iconScanRes: Int? = null, onSessionStarted: SessionStartedCallback) {
Log.session { "start card session with delegate" }
state = CardSessionState.Active
viewDelegate.onSessionStarted(cardId, initialMessage, environment.config.howToIsEnabled)
viewDelegate.onSessionStarted(cardId, initialMessage, environment.config.howToIsEnabled, iconScanRes)

reader.scope = scope
reader.startSession()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class BackupService(
is State.FinalizingPrimaryCard,
is State.FinalizingBackupCard,
-> true

else -> false
}

Expand All @@ -56,6 +57,7 @@ class BackupService(
val passcodeIsSet: Boolean get() = repo.data.passcode != null
val primaryCardIsSet: Boolean get() = repo.data.primaryCard != null
val primaryCardId: String? get() = repo.data.primaryCard?.cardId
val primaryPublicKey: ByteArray? get() = repo.data.primaryCard?.cardPublicKey
val backupCardIds: List<String> get() = repo.data.backupCards.map { it.cardId }

/**
Expand Down Expand Up @@ -126,10 +128,11 @@ class BackupService(
return CompletionResult.Success(Unit)
}

fun proceedBackup(callback: CompletionCallback<Card>) {
fun proceedBackup(iconScanRes: Int? = null, callback: CompletionCallback<Card>) {
when (val currentState = currentState) {
State.FinalizingPrimaryCard ->
handleFinalizePrimaryCard { result -> handleCompletion(result, callback) }
handleFinalizePrimaryCard(iconScanRes = iconScanRes) { result -> handleCompletion(result, callback) }

is State.FinalizingBackupCard ->
handleWriteBackupCard(currentState.index) { result ->
handleCompletion(result, callback)
Expand All @@ -145,7 +148,7 @@ class BackupService(
updateState()
}

fun readPrimaryCard(cardId: String? = null, callback: CompletionCallback<Unit>) {
fun readPrimaryCard(iconScanRes: Int? = null, cardId: String? = null, callback: CompletionCallback<Unit>) {
val formattedCardId = cardId?.let { CardIdFormatter(sdk.config.cardIdDisplayFormat).getFormattedCardId(it) }

val message = formattedCardId?.let {
Expand All @@ -159,12 +162,14 @@ class BackupService(
runnable = StartPrimaryCardLinkingTask(),
cardId = cardId,
initialMessage = message,
iconScanRes = iconScanRes,
) { result ->
when (result) {
is CompletionResult.Success -> {
setPrimaryCard(result.data)
callback(CompletionResult.Success(Unit))
}

is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
Expand All @@ -176,6 +181,7 @@ class BackupService(
updateState()
callback(result)
}

is CompletionResult.Failure -> callback(result)
}
}
Expand All @@ -184,10 +190,13 @@ class BackupService(
currentState = when {
repo.data.accessCode == null || repo.data.primaryCard == null || repo.data.backupCards.isEmpty() ->
State.Preparing

repo.data.attestSignature == null || repo.data.backupData.isEmpty() ->
State.FinalizingPrimaryCard

repo.data.finalizedBackupCardsCount < repo.data.backupCards.size ->
State.FinalizingBackupCard(repo.data.finalizedBackupCardsCount + 1)

else -> {
onBackupCompleted()
State.Finished
Expand Down Expand Up @@ -221,12 +230,13 @@ class BackupService(
addBackupCard(result.data)
callback(CompletionResult.Success(Unit))
}

is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
}

private fun handleFinalizePrimaryCard(callback: CompletionCallback<Card>) {
private fun handleFinalizePrimaryCard(iconScanRes: Int? = null, callback: CompletionCallback<Card>) {
try {
if (handleErrors && repo.data.accessCode == null && repo.data.passcode == null) {
throw TangemSdkError.AccessCodeOrPasscodeRequired()
Expand Down Expand Up @@ -278,6 +288,7 @@ class BackupService(
runnable = task,
cardId = primaryCard.cardId,
initialMessage = message,
iconScanRes = iconScanRes,
callback = callback,
)
} catch (error: TangemSdkError) {
Expand Down Expand Up @@ -347,6 +358,7 @@ class BackupService(
)
callback(CompletionResult.Success(result.data))
}

is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class StartPrimaryCardLinkingTask : CardSessionRunnable<PrimaryCard> {
is CompletionResult.Success -> {
loadIssuerSignature(result.data, session, callback)
}

is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
Expand Down Expand Up @@ -59,6 +60,7 @@ class StartPrimaryCardLinkingTask : CardSessionRunnable<PrimaryCard> {
val primaryCard = PrimaryCard(rawCard, issuerSignature = signature.hexToBytes())
callback(CompletionResult.Success(primaryCard))
}

is Result.Failure ->
callback(CompletionResult.Failure(TangemSdkError.IssuerSignatureLoadingFailed()))
}
Expand Down

0 comments on commit a015dec

Please sign in to comment.