diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0b2ede6c22..99243416f0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -79,6 +79,8 @@
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
index 06bd7b850e..e08ffeb499 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
@@ -108,6 +108,14 @@ fun Context.copyToClipboard(value: Any, label: String = "") {
clipboard.setPrimaryClip(clip)
}
+fun Context.getFromClipboard(default: CharSequence? = null): CharSequence? {
+ val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return default
+ val clipData = clipboard.primaryClip ?: return default
+ if (clipData.itemCount == 0) return default
+
+ return clipData.getItemAt(0).text
+}
+
fun Context.shareText(text: String) {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
diff --git a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt
new file mode 100644
index 0000000000..b350c9ebe4
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt
@@ -0,0 +1,65 @@
+package com.tangem.tap.common.qrCodeScan
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.google.zxing.Result
+import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE
+import com.tangem.tap.features.send.redux.AddressPayIdActionUI
+import com.tangem.tap.store
+import me.dm7.barcodescanner.zxing.ZXingScannerView
+
+/**
+[REDACTED_AUTHOR]
+ */
+class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
+ private lateinit var mScannerView: ZXingScannerView
+
+ override fun onCreate(state: Bundle?) {
+ super.onCreate(state)
+ mScannerView = ZXingScannerView(this)
+ setContentView(mScannerView)
+
+ if (!permissionIsGranted()) requestPermission()
+
+ }
+
+ override fun onResume() {
+ super.onResume()
+ mScannerView.setResultHandler(this)
+ mScannerView.startCamera()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ mScannerView.stopCamera()
+ }
+
+ override fun handleResult(result: Result) {
+ store.dispatch(AddressPayIdActionUI.SetAddressOrPayId(result.text))
+ finish()
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ if (requestCode != PERMISSION_REQUEST_CODE) return
+
+ if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ finish()
+ }
+ }
+
+ private fun permissionIsGranted(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
+ cameraPermission == PackageManager.PERMISSION_GRANTED
+ } else true
+ }
+
+ private fun requestPermission() {
+ ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
index 2a03f03e25..045c802a04 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
@@ -5,7 +5,7 @@ import com.tangem.tap.common.redux.navigation.NavigationState
import com.tangem.tap.common.redux.navigation.navigationMiddleware
import com.tangem.tap.features.home.redux.homeMiddleware
import com.tangem.tap.features.send.redux.SendState
-import com.tangem.tap.features.send.redux.logMiddleware
+import com.tangem.tap.features.send.redux.sendMiddleware
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.walletMiddleware
import org.rekotlin.Middleware
@@ -22,7 +22,7 @@ data class AppState(
fun getMiddleware(): List> {
return listOf(
logMiddleware, navigationMiddleware, notificationsMiddleware,
- homeMiddleware, walletMiddleware,
+ homeMiddleware, walletMiddleware, sendMiddleware
)
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt
similarity index 75%
rename from app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt
rename to app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt
index 3d6f5b59d6..9e7276f9bd 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt
@@ -1,6 +1,5 @@
-package com.tangem.tap.features.send.redux
+package com.tangem.tap.common.redux
-import com.tangem.tap.common.redux.AppState
import org.rekotlin.Middleware
import timber.log.Timber
diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
index 20669d7f3b..c3c591a1c4 100644
--- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
@@ -5,6 +5,7 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.features.send.redux.SendRelease
import com.tangem.tap.store
import kotlinx.android.synthetic.main.fragment_wallet.*
import org.rekotlin.StoreSubscriber
@@ -42,4 +43,9 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
storeSubscribersList.forEach { store.unsubscribe(it) }
super.onStop()
}
+
+ override fun onDestroy() {
+ store.dispatch(SendRelease)
+ super.onDestroy()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt
deleted file mode 100644
index 1e28b71713..0000000000
--- a/app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.tangem.tap.features.send.redux
-
-import org.rekotlin.Action
-
-/**
-[REDACTED_AUTHOR]
- */
-interface SendAction : Action
-
-sealed class FeeLayout : SendAction {
- object ToggleFeeLayoutVisibility : FeeLayout()
- data class ChangeSelectedFee(val id: Int) : FeeLayout()
- class ChangeIncludeFee(val isChecked: Boolean) : FeeLayout()
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt
new file mode 100644
index 0000000000..8b9800c188
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt
@@ -0,0 +1,86 @@
+package com.tangem.tap.features.send.redux
+
+import com.tangem.blockchain.common.WalletManager
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.domain.isPayIdSupported
+import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId
+import com.tangem.tap.scope
+import com.tangem.tap.store
+import kotlinx.coroutines.launch
+import org.rekotlin.Action
+import org.rekotlin.Middleware
+
+/**
+[REDACTED_AUTHOR]
+ */
+val sendMiddleware: Middleware = { dispatch, appState ->
+ { nextDispatch ->
+ { action ->
+ handleSendAction(action)
+ nextDispatch(action)
+ }
+ }
+}
+
+private fun handleSendAction(action: Action) {
+ val sendAction = action as? SendScreenActionUI ?: return
+
+ when (sendAction) {
+ is AddressPayIdActionUI -> {
+ when (sendAction) {
+ is SetAddressOrPayId -> AddressPayIdHandler().handle(sendAction.data?.toString())
+ }
+ }
+ }
+}
+
+internal class AddressPayIdHandler {
+ fun handle(data: String?) {
+ val walletManager = store.state.globalState.walletManager ?: return
+ val clipboardData = data ?: return
+
+ if (AddressPayIDState.isPayIDAddress(clipboardData)) {
+ if (walletManager.wallet.blockchain.isPayIdSupported()) {
+ store.dispatch(AddressPayIdAction.Verification.PayIdNotSupportedByBlockchain)
+ } else {
+ scope.launch {
+ val response = verifyPayID(walletManager, clipboardData)
+ if (response == null) {
+ store.dispatch(AddressPayIdAction.Verification.Failed)
+ } else {
+ val address = "some address D:" // extract from response
+ store.dispatch(AddressPayIdAction.Verification.Success(address))
+ }
+ }
+ }
+ } else {
+
+ }
+ }
+
+ private suspend fun verifyPayID(walletManager: WalletManager, payID: String?): String? {
+ val cardId = walletManager.cardId
+ val publicKey = store.state.globalState.card?.cardPublicKey
+
+// val result = PayIdManager().getPayId(cardId, publicKey.toHexString())
+// withContext(Dispatchers.Main) {
+// when (result) {
+// is Result.Success -> {
+// val payId = result.data
+// if (payId == null) {
+// store.dispatch(WalletAction.LoadPayId.NotCreated)
+// } else {
+// store.dispatch(WalletAction.LoadPayId.Success(payId))
+// }
+// }
+// is Result.Failure -> store.dispatch(WalletAction.LoadPayId.Failure)
+// }
+// }
+ return null
+ }
+
+ private suspend fun verifyWalletAddress(payID: String?): Boolean {
+ val isRealAddress = true
+ return isRealAddress
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt
index 69c58dde89..2cf6e9fe78 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt
@@ -1,37 +1,59 @@
package com.tangem.tap.features.send.redux
import android.view.View
+import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId
+import com.tangem.tap.features.send.redux.FeeActionUI.*
import org.rekotlin.Action
import org.rekotlin.StateType
+import timber.log.Timber
/**
[REDACTED_AUTHOR]
*/
class SendReducer {
companion object {
- fun reduce(action: Action, state: SendState): SendState {
- val sendAction = action as? SendAction ?: return state
+ fun reduce(action: Action, sendState: SendState): SendState {
+ if (action is SendRelease) return SendState()
+ val sendAction = action as? SendScreenAction ?: return sendState
return when (sendAction) {
- is FeeLayout -> handleFeeLayoutAction(sendAction, state, state.feeLayoutState)
- else -> state
+ is AddressPayIdActionUI -> handleAddressPayIdAction(sendAction, sendState, sendState.addressPayIDState)
+ is FeeActionUI -> handleFeeLayoutAction(sendAction, sendState, sendState.feeLayoutState)
+ else -> sendState
}
}
- private fun handleFeeLayoutAction(action: FeeLayout, sendState: SendState, state: FeeLayoutState): SendState {
+ private fun handleAddressPayIdAction(
+ action: AddressPayIdActionUI,
+ sendState: SendState,
+ state: AddressPayIDState
+ ): SendState {
+ var state = state
+
+ when (action) {
+ is SetAddressOrPayId -> {
+ state = state.copy(value = action.data?.toString())
+ return updateLastState(sendState.copy(addressPayIDState = state), state)
+ }
+ }
+
+ return sendState
+ }
+
+ private fun handleFeeLayoutAction(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState {
return when (action) {
- is FeeLayout.ToggleFeeLayoutVisibility -> {
+ is ToggleFeeLayoutVisibility -> {
val visibility = if (state.visibility == View.VISIBLE) View.GONE
else View.VISIBLE
val result = state.copy(visibility = visibility)
updateLastState(sendState.copy(feeLayoutState = result), result)
}
- is FeeLayout.ChangeSelectedFee -> {
+ is ChangeSelectedFee -> {
val result = state.copy(selectedFeeId = action.id)
updateLastState(sendState.copy(feeLayoutState = result), result)
}
- is FeeLayout.ChangeIncludeFee -> {
+ is ChangeIncludeFee -> {
val result = state.copy(includeFeeIsChecked = action.isChecked)
updateLastState(sendState.copy(feeLayoutState = result), result)
}
@@ -39,7 +61,9 @@ class SendReducer {
}
private fun updateLastState(sendState: SendState, state: StateType): SendState {
- return sendState.copy(lastChangedStateType = state)
+ val sendState = sendState.copy(lastChangedStateType = state)
+ Timber.d("$sendState")
+ return sendState
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt
new file mode 100644
index 0000000000..3da6e6acce
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt
@@ -0,0 +1,29 @@
+package com.tangem.tap.features.send.redux
+
+import org.rekotlin.Action
+
+/**
+[REDACTED_AUTHOR]
+ */
+interface SendScreenAction : Action
+interface SendScreenActionUI : SendScreenAction
+
+object SendRelease : Action
+
+sealed class FeeActionUI : SendScreenActionUI {
+ object ToggleFeeLayoutVisibility : FeeActionUI()
+ data class ChangeSelectedFee(val id: Int) : FeeActionUI()
+ class ChangeIncludeFee(val isChecked: Boolean) : FeeActionUI()
+}
+
+sealed class AddressPayIdActionUI : SendScreenActionUI {
+ data class SetAddressOrPayId(val data: CharSequence?) : AddressPayIdActionUI()
+}
+
+sealed class AddressPayIdAction : SendScreenAction {
+ object Verification : AddressPayIdAction() {
+ object PayIdNotSupportedByBlockchain : AddressPayIdAction()
+ object Failed : AddressPayIdAction()
+ data class Success(val payIdWalletAddress: String) : AddressPayIdAction()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt
index e2e5c5aa38..d17bab98c9 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux
import android.view.View
+import com.tangem.blockchain.common.WalletManager
import com.tangem.wallet.R
import org.rekotlin.StateType
@@ -8,12 +9,26 @@ import org.rekotlin.StateType
[REDACTED_AUTHOR]
*/
data class SendState(
+ val walletManager: WalletManager? = null,
val lastChangedStateType: StateType = NoneState(),
+ val addressPayIDState: AddressPayIDState = AddressPayIDState(),
val feeLayoutState: FeeLayoutState = FeeLayoutState()
) : StateType
class NoneState : StateType
+data class AddressPayIDState(
+ val value: String? = null,
+ val payIDWalletAddress: String? = null,
+ val error: String? = null,
+) : StateType {
+ fun isPayIDAddress(): Boolean = isPayIDAddress(value)
+
+ companion object {
+ fun isPayIDAddress(value: String?): Boolean = value?.contains("\$payid.tangem.com") ?: false
+ }
+}
+
data class FeeLayoutState(
val visibility: Int = View.GONE,
val selectedFeeId: Int = R.id.chipNormal,
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
index 40f2689a18..06929486da 100644
--- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
@@ -1,13 +1,19 @@
package com.tangem.tap.features.send.ui
+import android.content.Intent
import android.os.Bundle
import android.view.View
+import com.tangem.tap.common.extensions.getFromClipboard
+import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
import com.tangem.tap.features.send.BaseStoreFragment
-import com.tangem.tap.features.send.redux.FeeLayout
+import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId
+import com.tangem.tap.features.send.redux.FeeActionUI.*
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
import com.tangem.tap.features.send.ui.stateSubscribers.WalletStateSubscriber
import com.tangem.tap.store
import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.btn_paste.*
+import kotlinx.android.synthetic.main.btn_qr_code.*
import kotlinx.android.synthetic.main.layout_send_network_fee.*
/**
@@ -22,13 +28,20 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
super.onViewCreated(view, savedInstanceState)
flExpandCollapse.setOnClickListener {
- store.dispatch(FeeLayout.ToggleFeeLayoutVisibility)
+ store.dispatch(ToggleFeeLayoutVisibility)
}
chipGroup.setOnCheckedChangeListener { group, checkedId ->
- store.dispatch(FeeLayout.ChangeSelectedFee(checkedId))
+ store.dispatch(ChangeSelectedFee(checkedId))
}
swIncludeFee.setOnCheckedChangeListener { btn, isChecked ->
- store.dispatch(FeeLayout.ChangeIncludeFee(isChecked))
+ store.dispatch(ChangeIncludeFee(isChecked))
+ }
+
+ imvPaste.setOnClickListener {
+ store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()))
+ }
+ imvQrCode.setOnClickListener {
+ requireActivity().startActivity(Intent(requireContext(), ScanQrCodeActivity::class.java))
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
index 3f4ea153f4..d86fe32991 100644
--- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
@@ -3,11 +3,12 @@ package com.tangem.tap.features.send.ui.stateSubscribers
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.transition.TransitionManager
+import com.tangem.tap.features.send.redux.AddressPayIDState
import com.tangem.tap.features.send.redux.FeeLayoutState
import com.tangem.tap.features.send.redux.SendState
import kotlinx.android.synthetic.main.btn_expand_collapse.*
+import kotlinx.android.synthetic.main.layout_send_address_payid.*
import kotlinx.android.synthetic.main.layout_send_network_fee.*
-import timber.log.Timber
/**
[REDACTED_AUTHOR]
@@ -17,11 +18,15 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber handleFeeLayoutState(fg, state.feeLayoutState)
+ is AddressPayIDState -> handleAddressPayIdState(fg, state.addressPayIDState)
}
}
+ private fun handleAddressPayIdState(fg: Fragment, state: AddressPayIDState) {
+ fg.etAddressOrPayId.setText(state.value)
+ }
+
private fun handleFeeLayoutState(fg: Fragment, layoutState: FeeLayoutState) {
- Timber.d("handleFeeLayoutState")
if (fg.llFeeContainer.visibility != layoutState.visibility) {
val rotationAngle = if (fg.imvExpandCollapse.rotation == 0f) 180f else 0f
fg.imvExpandCollapse.rotation = rotationAngle