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/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt
index cbf1d9ccbb..b2947e0c13 100644
--- a/app/src/main/java/com/tangem/tap/MainActivity.kt
+++ b/app/src/main/java/com/tangem/tap/MainActivity.kt
@@ -1,7 +1,10 @@
package com.tangem.tap
+import android.app.Activity
import android.content.pm.ActivityInfo
+import android.os.Build
import android.os.Bundle
+import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import com.tangem.CardFilter
import com.tangem.Config
@@ -76,4 +79,15 @@ class MainActivity : AppCompatActivity() {
store.dispatch(NavigationAction.ActivityDestroyed)
super.onDestroy()
}
+}
+
+fun setTranslucent(activity: Activity, translucent: Boolean) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val w = activity.getWindow();
+ if (translucent) {
+ w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ } else {
+ w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt
index 4ab0e12db4..4b49e6ff0a 100644
--- a/app/src/main/java/com/tangem/tap/TapApplication.kt
+++ b/app/src/main/java/com/tangem/tap/TapApplication.kt
@@ -3,7 +3,10 @@ package com.tangem.tap
import android.app.Application
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer
+import com.tangem.tap.network.NetworkConnectivity
+import com.tangem.wallet.BuildConfig
import org.rekotlin.Store
+import timber.log.Timber
val store = Store(
reducer = ::appReducer,
@@ -11,4 +14,13 @@ val store = Store(
state = AppState()
)
-class TapApplication : Application()
\ No newline at end of file
+class TapApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+
+ if (BuildConfig.DEBUG) {
+ Timber.plant(Timber.DebugTree())
+ }
+ NetworkConnectivity(store, this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
index a1f13c87d4..1890cabce0 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
@@ -5,6 +5,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.home.HomeFragment
+import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.wallet.R
@@ -34,5 +35,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()
AppScreen.Wallet -> WalletFragment()
+ AppScreen.Send -> SendFragment()
}
}
\ 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 8e68a1bd6f..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
@@ -29,14 +29,17 @@ fun View.show(show: Boolean) {
}
fun View.show() {
+ if (this.visibility == View.VISIBLE) return
this.visibility = View.VISIBLE
}
fun View.hide() {
+ if (this.visibility == View.GONE) return
this.visibility = View.GONE
}
fun View.makeInvisible() {
+ if (this.visibility == View.INVISIBLE) return
this.visibility = View.INVISIBLE
}
@@ -105,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/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
index 1431c3b890..3888f22fcf 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
@@ -2,16 +2,19 @@ package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.common.redux.navigation.navigationReducer
+import com.tangem.tap.features.send.redux.SendReducer
import com.tangem.tap.features.wallet.redux.walletReducer
import org.rekotlin.Action
fun appReducer(action: Action, state: AppState?): AppState {
requireNotNull(state)
if (action is AppAction.RestoreState) return action.state
+
return AppState(
- navigationState = navigationReducer(action, state),
- globalState = globalReducer(action, state),
- walletState = walletReducer(action, state),
+ navigationState = navigationReducer(action, state),
+ globalState = globalReducer(action, state),
+ walletState = walletReducer(action, state),
+ sendState = SendReducer.reduce(action, state.sendState)
)
}
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 8b147b94f0..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
@@ -4,22 +4,25 @@ import com.tangem.tap.common.redux.global.GlobalState
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.sendMiddleware
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.walletMiddleware
import org.rekotlin.Middleware
import org.rekotlin.StateType
data class AppState(
- val navigationState: NavigationState = NavigationState(),
- val globalState: GlobalState = GlobalState(),
- val walletState: WalletState = WalletState()
+ val navigationState: NavigationState = NavigationState(),
+ val globalState: GlobalState = GlobalState(),
+ val walletState: WalletState = WalletState(),
+ val sendState: SendState = SendState(),
) : StateType {
companion object {
fun getMiddleware(): List> {
return listOf(
- navigationMiddleware, notificationsMiddleware,
- homeMiddleware, walletMiddleware,
+ logMiddleware, navigationMiddleware, notificationsMiddleware,
+ homeMiddleware, walletMiddleware, sendMiddleware
)
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt
new file mode 100644
index 0000000000..9e7276f9bd
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt
@@ -0,0 +1,16 @@
+package com.tangem.tap.common.redux
+
+import org.rekotlin.Middleware
+import timber.log.Timber
+
+/**
+[REDACTED_AUTHOR]
+ */
+val logMiddleware: Middleware = { dispatch, appState ->
+ { nextDispatch ->
+ { action ->
+ Timber.d("$action")
+ nextDispatch(action)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
index 72463f9da8..7fcff63909 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
@@ -9,4 +9,4 @@ data class NavigationState(
val activity: WeakReference? = null
) : StateType
-enum class AppScreen { Home, Wallet }
\ No newline at end of file
+enum class AppScreen { Home, Wallet, Send }
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
index 4a65bb702f..235f0ea37a 100644
--- a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
@@ -51,6 +51,8 @@ class PayIdManager {
}
companion object {
+ val payIdRegExp = "^[a-z0-9!#@%&*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#@%&*+/=?^_`{|}~-]+)*\\\$(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z-]*[a-z0-9])?|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})\$".toRegex()
+
val payIdSupported: EnumSet = EnumSet.of(
Blockchain.XRP,
Blockchain.Ethereum,
@@ -63,6 +65,8 @@ class PayIdManager {
Blockchain.Binance,
Blockchain.RSK,
)
+
+ fun isPayId(value: String?): Boolean = value?.contains(payIdRegExp) ?: false
}
}
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
new file mode 100644
index 0000000000..20669d7f3b
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
@@ -0,0 +1,45 @@
+package com.tangem.tap.features.send
+
+import android.os.Bundle
+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.store
+import kotlinx.android.synthetic.main.fragment_wallet.*
+import org.rekotlin.StoreSubscriber
+
+/**
+[REDACTED_AUTHOR]
+ */
+abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
+
+ abstract fun subscribeToStore()
+
+ protected val storeSubscribersList = mutableListOf>()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ store.dispatch(NavigationAction.PopBackTo())
+ }
+ })
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ toolbar.setNavigationOnClickListener { store.dispatch(NavigationAction.PopBackTo()) }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ subscribeToStore()
+ }
+
+
+ override fun onStop() {
+ storeSubscribersList.forEach { store.unsubscribe(it) }
+ super.onStop()
+ }
+}
\ 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..f734d0816c
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt
@@ -0,0 +1,87 @@
+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.PayIdManager
+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 (PayIdManager.isPayId(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
new file mode 100644
index 0000000000..67a2f55308
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt
@@ -0,0 +1,71 @@
+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, sendState: SendState): SendState = internalReduce(action, sendState)
+ }
+}
+
+private fun internalReduce(action: Action, sendState: SendState): SendState {
+ if (action is ReleaseSendState) return SendState()
+ val sendAction = action as? SendScreenAction ?: return sendState
+
+ return when (sendAction) {
+ is AddressPayIdActionUI -> handleAddressPayIdAction(sendAction, sendState, sendState.addressPayIDState)
+ is FeeActionUI -> handleFeeLayoutAction(sendAction, sendState, sendState.feeLayoutState)
+ else -> 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 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 ChangeSelectedFee -> {
+ val result = state.copy(selectedFeeId = action.id)
+ updateLastState(sendState.copy(feeLayoutState = result), result)
+ }
+ is ChangeIncludeFee -> {
+ val result = state.copy(includeFeeIsChecked = action.isChecked)
+ updateLastState(sendState.copy(feeLayoutState = result), result)
+ }
+ }
+}
+
+private fun updateLastState(sendState: SendState, state: StateType): SendState {
+ 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..6e0075913b
--- /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 ReleaseSendState : 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
new file mode 100644
index 0000000000..70036db005
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt
@@ -0,0 +1,30 @@
+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
+
+/**
+[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
+
+data class FeeLayoutState(
+ val visibility: Int = View.GONE,
+ val selectedFeeId: Int = R.id.chipNormal,
+ val includeFeeIsChecked: Boolean = false
+) : StateType
\ No newline at end of file
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
new file mode 100644
index 0000000000..1879d895a8
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
@@ -0,0 +1,68 @@
+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.AddressPayIdActionUI.SetAddressOrPayId
+import com.tangem.tap.features.send.redux.FeeActionUI.*
+import com.tangem.tap.features.send.redux.ReleaseSendState
+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.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
+
+ private val sendSubscriber = SendStateSubscriber(this)
+ private val walletSubscriber = WalletStateSubscriber(this)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ flExpandCollapse.setOnClickListener {
+ store.dispatch(ToggleFeeLayoutVisibility)
+ }
+ chipGroup.setOnCheckedChangeListener { group, checkedId ->
+ store.dispatch(ChangeSelectedFee(checkedId))
+ }
+ swIncludeFee.setOnCheckedChangeListener { btn, isChecked ->
+ store.dispatch(ChangeIncludeFee(isChecked))
+ }
+
+ imvPaste.setOnClickListener {
+ store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()))
+ }
+ imvQrCode.setOnClickListener {
+ requireActivity().startActivity(Intent(requireContext(), ScanQrCodeActivity::class.java))
+ }
+ }
+
+ override fun subscribeToStore() {
+ store.subscribe(walletSubscriber) { appState ->
+ appState.skipRepeats { oldState, newState -> false }.select { it.walletState }
+ }
+ store.subscribe(sendSubscriber) { appState ->
+ appState.skipRepeats { oldState, newState -> false }.select { it.sendState }
+ }
+
+ storeSubscribersList.add(walletSubscriber)
+ storeSubscribersList.add(sendSubscriber)
+ }
+
+ override fun onDestroy() {
+ store.dispatch(ReleaseSendState)
+ super.onDestroy()
+ }
+}
+
+
+
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt
new file mode 100644
index 0000000000..58949c7df7
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt
@@ -0,0 +1,21 @@
+package com.tangem.tap.features.send.ui.stateSubscribers
+
+import androidx.fragment.app.Fragment
+import org.rekotlin.StateType
+import org.rekotlin.StoreSubscriber
+import java.lang.ref.WeakReference
+
+/**
+[REDACTED_AUTHOR]
+ */
+abstract class FragmentStateSubscriber(fragment: Fragment) : StoreSubscriber {
+ private val weakFragment: WeakReference = WeakReference(fragment)
+
+ abstract fun updateWithNewState(fg: Fragment, state: S)
+
+ override fun newState(state: S) {
+ val fg = weakFragment.get() ?: return
+
+ updateWithNewState(fg, state)
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000000..d86fe32991
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
@@ -0,0 +1,46 @@
+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.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber(fragment) {
+
+ override fun updateWithNewState(fg: Fragment, state: SendState) {
+ when (state.lastChangedStateType) {
+ is FeeLayoutState -> 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) {
+ if (fg.llFeeContainer.visibility != layoutState.visibility) {
+ val rotationAngle = if (fg.imvExpandCollapse.rotation == 0f) 180f else 0f
+ fg.imvExpandCollapse.rotation = rotationAngle
+
+ (fg.llFeeContainer.parent?.parent as? ViewGroup)?.let { TransitionManager.beginDelayedTransition(it) }
+ fg.llFeeContainer.visibility = layoutState.visibility
+ }
+
+ if (fg.swIncludeFee.isChecked != layoutState.includeFeeIsChecked) {
+ fg.swIncludeFee.isChecked = layoutState.includeFeeIsChecked
+ }
+
+ if (fg.chipGroup.checkedChipId != layoutState.selectedFeeId) {
+ fg.chipGroup.check(layoutState.selectedFeeId)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/WalletStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/WalletStateSubscriber.kt
new file mode 100644
index 0000000000..980f39ba73
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/WalletStateSubscriber.kt
@@ -0,0 +1,13 @@
+package com.tangem.tap.features.send.ui.stateSubscribers
+
+import androidx.fragment.app.Fragment
+import com.tangem.tap.features.wallet.redux.WalletState
+
+/**
+[REDACTED_AUTHOR]
+ */
+class WalletStateSubscriber(fragment: Fragment) : FragmentStateSubscriber(fragment) {
+ override fun updateWithNewState(fg: Fragment, state: WalletState) {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
index 1f3584bfa3..1c4c43a098 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
@@ -6,6 +6,7 @@ import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
+import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.PayIdState
@@ -55,6 +56,9 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber,
+ context: Context
+) {
+ private val connectivityManager = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(c: Context?, intent: Intent?) {
+ store.dispatch(NetworkStateChanged(isOnlineOrConnecting()))
+ }
+ }
+
+ init {
+ val intentFilter = IntentFilter()
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
+ context.registerReceiver(receiver, intentFilter)
+ store.dispatch(NetworkStateChanged(isOnlineOrConnecting()))
+ }
+
+
+ fun isOnlineOrConnecting(): Boolean {
+ val netInfo = connectivityManager.activeNetworkInfo
+ return netInfo != null && netInfo.isConnectedOrConnecting
+ }
+}
+
+data class NetworkStateChanged(val isOnline: Boolean) : Action
\ No newline at end of file
diff --git a/app/src/main/res/color/selector_chip_background.xml b/app/src/main/res/color/selector_chip_background.xml
index 14a32680dd..21a6b77707 100644
--- a/app/src/main/res/color/selector_chip_background.xml
+++ b/app/src/main/res/color/selector_chip_background.xml
@@ -1,9 +1,9 @@
-
-
-
+
+
+
diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml
index f0b7ab503a..0768fc3cf5 100644
--- a/app/src/main/res/layout/fragment_send.xml
+++ b/app/src/main/res/layout/fragment_send.xml
@@ -3,49 +3,70 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_marginTop="24dp"
android:orientation="vertical">
-
+ android:layout_height="?attr/actionBarSize"
+ app:layout_constraintTop_toTopOf="parent"
+ app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
+ app:title="@string/send_title" />
-
+ android:layout_height="wrap_content">
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_send_network_fee.xml b/app/src/main/res/layout/layout_send_network_fee.xml
index 156e8a2016..7daf515718 100644
--- a/app/src/main/res/layout/layout_send_network_fee.xml
+++ b/app/src/main/res/layout/layout_send_network_fee.xml
@@ -11,14 +11,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:text="@string/send_network_fee"
+ android:text="@string/send_fee"
android:textSize="13sp"
- app:layout_constraintBottom_toBottomOf="@+id/include"
+ app:layout_constraintBottom_toBottomOf="@+id/flExpandCollapse"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/include" />
+ app:layout_constraintTop_toTopOf="parent" />
-
+ app:layout_constraintTop_toBottomOf="@+id/flExpandCollapse">
-
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:translationY="-8dp"
+ app:singleLine="true"
+ app:singleSelection="true">
-
+
+
+
+
+
+
+
+
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="8dp"
+ android:text="" />
-
+
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a2fe09cfc5..9f30e32ccb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -34,11 +34,13 @@
Error response while creating PayID.
+ Send
Address or PayID
- Network fee
- Low
- Normal
- Priority
+ Network fee
+ Include fee
+ Low
+ Normal
+ Priority
Send
Total
Amount