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