diff --git a/tangem-sdk-new/build.gradle b/tangem-sdk-new/build.gradle index 3eee0d71a4..401ee8064e 100644 --- a/tangem-sdk-new/build.gradle +++ b/tangem-sdk-new/build.gradle @@ -38,9 +38,9 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.skyfishjy.ripplebackground:library:1.0.1' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.core:core-ktx:1.1.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' implementation "androidx.lifecycle:lifecycle-runtime:2.1.0" implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0" diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt index 6fe36f3a39..7db8003f6d 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt @@ -1,66 +1,117 @@ package com.tangem.tangem_sdk_new +import android.view.View import androidx.fragment.app.FragmentActivity import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.CardManagerDelegate import com.tangem.Log -import com.tangem.tasks.Task +import com.tangem.LoggerInterface +import com.tangem.common.CompletionResult +import com.tangem.tangem_sdk_new.nfc.NfcReader +import com.tangem.tangem_sdk_new.ui.NfcEnableDialog +import com.tangem.tangem_sdk_new.ui.TouchCardAnimation import com.tangem.tasks.TaskError import kotlinx.android.synthetic.main.layout_touch_card.* +import kotlinx.android.synthetic.main.nfc_bottom_sheet.* -class DefaultCardManagerDelegate(private val activity: FragmentActivity, private val reader: NfcReader) : CardManagerDelegate { +class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDelegate { - private var dialog: BottomSheetDialog? = null + lateinit var activity: FragmentActivity + private var readingDialog: BottomSheetDialog? = null + private var nfcEnableDialog: NfcEnableDialog? = null - private var task: Task<*>? = null - - override fun showSecurityDelay(ms: Int) { -// activity.runOnUiThread{ -// activity.tv_security_delay?.text = "Security delay. $ms left." -// } + init { + setLogger() } - override fun requestPin(success: () -> String, error: (cardError: TaskError.CardError) -> TaskError.CardError) { + override fun onTaskStarted() { +// reader.isoDep = null + reader.readingCancelled = false + postUI { showReadingDialog(activity) } + if (!reader.nfcEnabled) showNFCEnableDialog() + } + + private fun showReadingDialog(activity: FragmentActivity) { + val dialogView = activity.getLayoutInflater().inflate(R.layout.nfc_bottom_sheet, null) + readingDialog = BottomSheetDialog(activity) + readingDialog?.setContentView(dialogView) + readingDialog?.dismissWithAnimation = true + readingDialog?.create() + readingDialog?.setOnShowListener { + readingDialog?.rippleBackgroundNfc?.startRippleAnimation() + val nfcDeviceAntenna = TouchCardAnimation( + activity, readingDialog!!.ivHandCardHorizontal, + readingDialog!!.ivHandCardVertical, readingDialog!!.llHand, readingDialog!!.llNfc) + nfcDeviceAntenna.init() + } + readingDialog?.setOnCancelListener { + reader.readingCancelled = true +// reader.closeSession() + Log.i(this::class.simpleName!!, "readingCancelled is set to true") + } + readingDialog?.show() + } + + private fun showNFCEnableDialog() { + nfcEnableDialog = NfcEnableDialog() + activity.supportFragmentManager.let { nfcEnableDialog?.show(it, NfcEnableDialog.TAG) } + } + + override fun showSecurityDelay(ms: Int) { + postUI { + readingDialog?.lTouchCard?.visibility = View.GONE + readingDialog?.tvRemainingTime?.text = ms.div(100).toString() + readingDialog?.flSecurityDelay?.visibility = View.VISIBLE + readingDialog?.tvTaskTitle?.text = activity.getText(R.string.dialog_security_delay) + readingDialog?.tvTaskText?.text = + activity.getText(R.string.dialog_security_delay_description) + } + } + + override fun onTaskCompleted() { + reader.closeSession() + postUI { + readingDialog?.lTouchCard?.visibility = View.GONE + readingDialog?.flSecurityDelay?.visibility = View.GONE + readingDialog?.flCompletion?.visibility = View.VISIBLE + readingDialog?.ivCompletion?.setImageDrawable(activity.getDrawable(R.drawable.ic_done_135dp)) + } + postUI(300) { readingDialog?.dismiss() } + } + + override fun onTaskError(error: TaskError?) { + reader.closeSession() + postUI { + readingDialog?.lTouchCard?.visibility = View.GONE + readingDialog?.flSecurityDelay?.visibility = View.GONE + readingDialog?.flCompletion?.visibility = View.VISIBLE + readingDialog?.ivCompletion?.setImageDrawable(activity.getDrawable(R.drawable.ic_error_outline_135dp)) + readingDialog?.tvTaskTitle?.text = activity.getText(R.string.dialog_error) + readingDialog?.tvTaskText?.text = if (error != null) error::class.simpleName else "" + } + } + + override fun requestPin(callback: (result: CompletionResult) -> Unit) { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun openNfcPopup() { + private fun setLogger() { + Log.setLogger( + object : LoggerInterface { + override fun i(logTag: String, message: String) { + android.util.Log.i(logTag, message) + } - activity.runOnUiThread { - val dialogView = activity.getLayoutInflater().inflate(R.layout.nfc_bottom_sheet, null) - - dialog = BottomSheetDialog(activity) - dialog?.setContentView(dialogView) - dialog?.dismissWithAnimation - dialog?.create() -// .setTitle("NFC reader mode") - dialog?.setOnShowListener { - dialog!!.rippleBackgroundNfc?.startRippleAnimation() - - // init NFC Antenna - val nfcDeviceAntenna = TouchCardAnimation( - activity, dialog!!.ivHandCardHorizontal, - dialog!!.ivHandCardVertical, dialog!!.llHand, dialog!!.llNfc) - nfcDeviceAntenna.init() - } - dialog?.setOnDismissListener { - reader.readingCancelled = false - task = null - Log.i(this::class.simpleName!!, "readingCancelled is set to true") - } - dialog?.show() - } - Log.i(this::class.simpleName!!, "Nfc PopUp is opened") - } - - override fun closeNfcPopup() { - task = null - activity.runOnUiThread { - dialog?.dismiss() - } - Log.i(this::class.simpleName!!, "Nfc PopUp is closed") + override fun e(logTag: String, message: String) { + android.util.Log.e(logTag, message) + } + override fun v(logTag: String, message: String) { + android.util.Log.v(logTag, message) + } + } + ) } } \ No newline at end of file diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt index 19d42cc6c1..f8caac7feb 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLifecycleObserver.kt @@ -1,28 +1,26 @@ package com.tangem.tangem_sdk_new -import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.OnLifecycleEvent +import com.tangem.tangem_sdk_new.nfc.NfcManager -class NfcLifecycleObserver(private var nfcManager: NfcManager) : DefaultLifecycleObserver { +class NfcLifecycleObserver(private var nfcManager: NfcManager) : LifecycleObserver { - override fun onResume(owner: LifecycleOwner) { - super.onResume(owner) + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun onResume(owner: LifecycleOwner) { nfcManager.onResume() } - override fun onPause(owner: LifecycleOwner) { - super.onPause(owner) + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + fun onPause(owner: LifecycleOwner) { nfcManager.onPause() } - override fun onStop(owner: LifecycleOwner) { - super.onStop(owner) - nfcManager.onStop() - } - - override fun onDestroy(owner: LifecycleOwner) { - super.onDestroy(owner) + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun onDestroy(owner: LifecycleOwner) { nfcManager.onDestroy() } } \ No newline at end of file diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/Utils.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/Utils.kt index 3340a96626..491f7a1a52 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/Utils.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/Utils.kt @@ -3,9 +3,8 @@ package com.tangem.tangem_sdk_new import android.os.Handler import android.os.Looper - val uiHandler = Handler(Looper.getMainLooper()) -fun postUI(msTime: Long = 0, func: () -> Unit) { + internal fun postUI(msTime: Long = 0, func: () -> Unit) { if (msTime > 0) uiHandler.postDelayed({ func() }, msTime) else uiHandler.post(func) } \ No newline at end of file diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcManager.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt similarity index 68% rename from tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcManager.kt rename to tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt index f9221b0782..208c42bd5d 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcManager.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcManager.kt @@ -1,4 +1,4 @@ -package com.tangem.tangem_sdk_new +package com.tangem.tangem_sdk_new.nfc import android.content.BroadcastReceiver import android.content.Context @@ -10,20 +10,17 @@ import android.nfc.tech.IsoDep import android.os.Build import android.os.Bundle import androidx.fragment.app.FragmentActivity +import com.tangem.Log -class NfcManager(private val reader: NfcReader) : NfcAdapter.ReaderCallback { - +class NfcManager : NfcAdapter.ReaderCallback { + val reader = NfcReader() private var activity: FragmentActivity? = null private var nfcAdapter: NfcAdapter? = null - private var readerCallback: NfcAdapter.ReaderCallback? = null -// private var nfcEnableDialog: NfcEnableDialog? = null - - fun setCurrentActivity(activity: FragmentActivity, readerCallback: NfcAdapter.ReaderCallback) { + fun setCurrentActivity(activity: FragmentActivity) { this.activity = activity nfcAdapter = NfcAdapter.getDefaultAdapter(activity) - this.readerCallback = readerCallback } private val mBroadcastReceiver = object : BroadcastReceiver() { @@ -37,7 +34,7 @@ class NfcManager(private val reader: NfcReader) : NfcAdapter.ReaderCallback { } override fun onTagDiscovered(tag: Tag?) { -// reader.isoDep = IsoDep.get(tag) + Log.i(this::class.simpleName!!, "Nfc tag is discovered") if (reader.readingActive) reader.onTagDiscovered(tag) else ignoreTag(tag) } @@ -46,10 +43,12 @@ class NfcManager(private val reader: NfcReader) : NfcAdapter.ReaderCallback { val filter = IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED) activity?.registerReceiver(mBroadcastReceiver, filter) - if (nfcAdapter == null || nfcAdapter?.isEnabled != true) -// showNFCEnableDialog() - else + if ((nfcAdapter == null || nfcAdapter?.isEnabled != true)) { + reader.nfcEnabled = false + } else { + reader.nfcEnabled = true enableReaderMode() + } } fun onPause() { @@ -57,31 +56,22 @@ class NfcManager(private val reader: NfcReader) : NfcAdapter.ReaderCallback { disableReaderMode() } - fun onStop() { -// nfcEnableDialog?.dismiss() - } - fun onDestroy() { activity = null nfcAdapter = null - readerCallback = null } -// private fun showNFCEnableDialog() { -// nfcEnableDialog = NfcEnableDialog() -// activity?.supportFragmentManager?.let { nfcEnableDialog?.show(it, NfcEnableDialog.TAG) } -// } - private fun enableReaderMode() { val options = Bundle() - nfcAdapter?.enableReaderMode(activity, readerCallback, READER_FLAGS, options) + nfcAdapter?.enableReaderMode(activity, activity as NfcAdapter.ReaderCallback, READER_FLAGS, options) } private fun disableReaderMode() { nfcAdapter?.disableReaderMode(activity) } - fun ignoreTag(tag: Tag?) { + private fun ignoreTag(tag: Tag?) { + Log.i(this::class.simpleName!!, "Nfc tag is ignored") if (Build.VERSION.SDK_INT >= 24) { nfcAdapter?.ignore(tag, 1500, null, null) } diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcReader.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt similarity index 62% rename from tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcReader.kt rename to tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt index 8aae5f33a6..fef45ceb69 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcReader.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt @@ -1,4 +1,4 @@ -package com.tangem.tangem_sdk_new +package com.tangem.tangem_sdk_new.nfc import android.nfc.Tag import android.nfc.tech.IsoDep @@ -11,19 +11,29 @@ import com.tangem.tasks.TaskError class NfcReader : CardReader { + override var readingActive = false + var nfcEnabled = false var isoDep: IsoDep? = null set(value) { + // if tag is received, call connect first before transceiving data if (field == null) { field = value connect() } + // don't reassign when there's an active tag already + if (value == null) field = value } - override var readingActive = false + var readingCancelled = false - set(value) { - if (value) readingActive = false - field = value - } + set(value) { + field = value + if (value) { + // Stops reading and sends failure callback to a task + // if reading is cancelled (when user closes nfc bottom sheet dialog). + readingActive = false + callback?.invoke(CompletionResult.Failure(TaskError.UserCancelledError())) + } + } var data: ByteArray? = null var callback: ((response: CompletionResult) -> Unit)? = null @@ -33,15 +43,16 @@ class NfcReader : CardReader { readingCancelled = false } + override fun closeSession() { + isoDep = null + readingActive = false + } + override fun transceiveApdu(apdu: CommandApdu, callback: (response: CompletionResult) -> Unit) { data = apdu.apduData this.callback = callback if (isoDep != null) { - try { - transceiveData() - } catch (exception: Exception) { - isoDep = null - } + transceiveData() } } @@ -50,11 +61,18 @@ class NfcReader : CardReader { callback?.invoke(CompletionResult.Failure(TaskError.UserCancelledError())) return } - val rawResponse = isoDep?.transceive(data) + if (data == null) return + + val rawResponse: ByteArray? + try { + rawResponse = isoDep?.transceive(data) + } catch (exception: Exception) { + isoDep = null + return + } if (rawResponse != null) { Log.i(this::class.simpleName!!, "Nfc response is received") data = null - readingActive = false } rawResponse?.let { callback?.invoke(CompletionResult.Success(ResponseApdu(it))) } } @@ -69,7 +87,7 @@ class NfcReader : CardReader { isoDep?.connect() isoDep?.close() isoDep?.connect() - isoDep?.timeout = 120000 + isoDep?.timeout = 240000 Log.i(this::class.simpleName!!, "Nfc session is started") } diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcEnableDialog.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcEnableDialog.kt new file mode 100644 index 0000000000..bbdf00079d --- /dev/null +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcEnableDialog.kt @@ -0,0 +1,30 @@ +package com.tangem.tangem_sdk_new.ui + +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import android.provider.Settings + +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.tangem.tangem_sdk_new.R + +class NfcEnableDialog : DialogFragment() { + companion object { + val TAG: String = NfcEnableDialog::class.java.simpleName + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(requireContext()) + builder.setCancelable(false) + .setIcon(R.drawable.ic_action_nfc_gray) + .setTitle(R.string.dialog_nfc_enable_title) + .setMessage(R.string.dialog_nfc_enable_text) + .setPositiveButton(R.string.general_ok + ) { _, _ -> + activity?.startActivity(Intent(Settings.ACTION_NFC_SETTINGS)) + } + return builder.create() + } + +} \ No newline at end of file diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLocation.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcLocation.kt similarity index 99% rename from tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLocation.kt rename to tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcLocation.kt index e51111ec5b..113c002fe9 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/NfcLocation.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/NfcLocation.kt @@ -1,4 +1,4 @@ -package com.tangem.tangem_sdk_new +package com.tangem.tangem_sdk_new.ui enum class NfcLocation(val codename: String, val fullName: String, val orientation: Int, val x: Int, val y: Int, val z: Int) { model1("sailfish", "Google Pixel", 0, 65, 25, 0), diff --git a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/TouchCardAnimation.kt b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/TouchCardAnimation.kt similarity index 93% rename from tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/TouchCardAnimation.kt rename to tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/TouchCardAnimation.kt index 78fe8fec34..717c0cf827 100644 --- a/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/TouchCardAnimation.kt +++ b/tangem-sdk-new/src/main/java/com/tangem/tangem_sdk_new/ui/TouchCardAnimation.kt @@ -1,4 +1,4 @@ -package com.tangem.tangem_sdk_new +package com.tangem.tangem_sdk_new.ui import android.content.Context import android.os.Build @@ -39,7 +39,7 @@ class TouchCardAnimation(private var context: Context, val lp = llHand.layoutParams as RelativeLayout.LayoutParams val lp2 = llNfc.layoutParams as RelativeLayout.LayoutParams val dp = context.resources.displayMetrics.density - val lm = dp * (69 + x * 75) + val lm = dp * (47 + x * 75) lp.topMargin = (dp * (-100 + y * 250)).toInt() lp2.topMargin = (dp * (-125 + y * 250)).toInt() llNfc.layoutParams = lp2 @@ -91,8 +91,8 @@ class TouchCardAnimation(private var context: Context, // set card z position when (z) { - TouchCardAnimation.CARD_ON_BACK -> llHand.elevation = 0.0f - TouchCardAnimation.CARD_ON_FRONT -> llHand.elevation = 30.0f + CARD_ON_BACK -> llHand.elevation = 0.0f + CARD_ON_FRONT -> llHand.elevation = 30.0f } } diff --git a/tangem-sdk-new/src/main/res/drawable/ic_action_nfc_gray.png b/tangem-sdk-new/src/main/res/drawable/ic_action_nfc_gray.png new file mode 100644 index 0000000000..22837067bf Binary files /dev/null and b/tangem-sdk-new/src/main/res/drawable/ic_action_nfc_gray.png differ diff --git a/tangem-sdk-new/src/main/res/drawable/ic_done_135dp.xml b/tangem-sdk-new/src/main/res/drawable/ic_done_135dp.xml new file mode 100644 index 0000000000..8e21e495e9 --- /dev/null +++ b/tangem-sdk-new/src/main/res/drawable/ic_done_135dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/tangem-sdk-new/src/main/res/drawable/ic_error_outline_135dp.xml b/tangem-sdk-new/src/main/res/drawable/ic_error_outline_135dp.xml new file mode 100644 index 0000000000..f69fac3ce0 --- /dev/null +++ b/tangem-sdk-new/src/main/res/drawable/ic_error_outline_135dp.xml @@ -0,0 +1,4 @@ + + + diff --git a/tangem-sdk-new/src/main/res/layout/layout_touch_card.xml b/tangem-sdk-new/src/main/res/layout/layout_touch_card.xml index 6ba9b2c053..c8f66c0bc4 100644 --- a/tangem-sdk-new/src/main/res/layout/layout_touch_card.xml +++ b/tangem-sdk-new/src/main/res/layout/layout_touch_card.xml @@ -1,11 +1,9 @@ + android:gravity="center_vertical|center_horizontal"> - - - - - - - - - - \ No newline at end of file diff --git a/tangem-sdk-new/src/main/res/layout/nfc_bottom_sheet.xml b/tangem-sdk-new/src/main/res/layout/nfc_bottom_sheet.xml index 8cd04b73f1..8e3d94ddd3 100644 --- a/tangem-sdk-new/src/main/res/layout/nfc_bottom_sheet.xml +++ b/tangem-sdk-new/src/main/res/layout/nfc_bottom_sheet.xml @@ -1,18 +1,77 @@ - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tangem-sdk-new/src/main/res/values/strings.xml b/tangem-sdk-new/src/main/res/values/strings.xml index 00f45c6bc6..f9ab1c5567 100644 --- a/tangem-sdk-new/src/main/res/values/strings.xml +++ b/tangem-sdk-new/src/main/res/values/strings.xml @@ -1,4 +1,10 @@ Tangem Sdk - READING… + OK + Enable NFC? + This app is useless when NFC is disabled. Reader mode depends on it. Hit OK to go to Settings, where you can enable NFC. + Security delay + This banknote has enforced security delay. Please hold the banknote firmly until the operation is completed… + Error +