From 14f4b46e1974486392cb159ab795ce2f03621744 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Sep 2020 21:38:18 +0300 Subject: [PATCH 1/4] Updated on 2026-08-14 --- app/build.gradle | 2 +- .../main/java/com/tangem/tap/MainActivity.kt | 14 +++ .../java/com/tangem/tap/TapApplication.kt | 14 ++- .../tap/common/extensions/Navigation.kt | 2 + .../com/tangem/tap/common/extensions/UI.kt | 3 + .../com/tangem/tap/common/redux/AppReducer.kt | 9 +- .../com/tangem/tap/common/redux/AppState.kt | 11 ++- .../redux/navigation/NavigationState.kt | 2 +- .../tap/features/send/BaseStoreFragment.kt | 45 +++++++++ .../tap/features/send/redux/LogMiddleware.kt | 17 ++++ .../tap/features/send/redux/SendAction.kt | 14 +++ .../tap/features/send/redux/SendReducer.kt | 45 +++++++++ .../tap/features/send/redux/SendState.kt | 21 +++++ .../tap/features/send/ui/SendFragment.kt | 49 ++++++++++ .../FragmentStateSubscriber.kt | 21 +++++ .../stateSubscribers/SendStateSubscriber.kt | 41 +++++++++ .../stateSubscribers/WalletStateSubscriber.kt | 13 +++ .../tap/features/wallet/ui/WalletFragment.kt | 4 + .../tangem/tap/network/NetworkConnectivity.kt | 40 ++++++++ .../res/color/selector_chip_background.xml | 6 +- app/src/main/res/layout/fragment_send.xml | 91 ++++++++++++------- .../res/layout/layout_send_network_fee.xml | 85 +++++++++-------- app/src/main/res/values/strings.xml | 1 + 23 files changed, 463 insertions(+), 87 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/WalletStateSubscriber.kt create mode 100644 app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt diff --git a/app/build.gradle b/app/build.gradle index 04a330ca2b..d363ee436d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,7 +64,7 @@ dependencies { implementation 'com.google.android.material:material:1.2.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' - implementation 'com.tangem:blockchain:1.25.0' + implementation 'com.tangem:blockchain:1.27.0' //lifecycle implementation "androidx.lifecycle:lifecycle-runtime:2.2.0" 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..06bd7b850e 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 } 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..2a03f03e25 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,21 +4,24 @@ 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.logMiddleware 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, + logMiddleware, navigationMiddleware, notificationsMiddleware, homeMiddleware, walletMiddleware, ) } 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/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/LogMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt new file mode 100644 index 0000000000..3d6f5b59d6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/redux/LogMiddleware.kt @@ -0,0 +1,17 @@ +package com.tangem.tap.features.send.redux + +import com.tangem.tap.common.redux.AppState +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/features/send/redux/SendAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt new file mode 100644 index 0000000000..6621539b8d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt @@ -0,0 +1,14 @@ +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 ChangeLoremState(val isChecked: Boolean) : FeeLayout() +} \ 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..7e70ffe625 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt @@ -0,0 +1,45 @@ +package com.tangem.tap.features.send.redux + +import android.view.View +import org.rekotlin.Action +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +class SendReducer { + companion object { + fun reduce(action: Action, state: SendState): SendState { + val sendAction = action as? SendAction ?: return state + + return when (sendAction) { + is FeeLayout -> handleFeeLayoutAction(sendAction, state, state.feeLayoutState) + else -> state + } + } + + private fun handleFeeLayoutAction(action: FeeLayout, sendState: SendState, state: FeeLayoutState): SendState { + return when (action) { + is FeeLayout.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 -> { + val result = state.copy(selectedFeeId = action.id) + updateLastState(sendState.copy(feeLayoutState = result), result) + } + is FeeLayout.ChangeLoremState -> { + val result = state.copy(swLoremIsChecked = action.isChecked) + updateLastState(sendState.copy(feeLayoutState = result), result) + } + } + } + + private fun updateLastState(sendState: SendState, state: StateType): SendState { + return sendState.copy(lastChangedStateType = state) + } + } +} 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..9baf474de8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.features.send.redux + +import android.view.View +import com.tangem.wallet.R +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class SendState( + val lastChangedStateType: StateType = NoneState(), + val feeLayoutState: FeeLayoutState = FeeLayoutState() +) : StateType + +class NoneState : StateType + +data class FeeLayoutState( + val visibility: Int = View.GONE, + val selectedFeeId: Int = R.id.chipNormal, + val swLoremIsChecked: 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..f7880fec0e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -0,0 +1,49 @@ +package com.tangem.tap.features.send.ui + +import android.os.Bundle +import android.view.View +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.send.redux.FeeLayout +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.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(FeeLayout.ToggleFeeLayoutVisibility) + } + chipGroup.setOnCheckedChangeListener { group, checkedId -> + store.dispatch(FeeLayout.ChangeSelectedFee(checkedId)) + } + swLorem.setOnCheckedChangeListener { btn, isChecked -> + store.dispatch(FeeLayout.ChangeLoremState(isChecked)) + } + } + + 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) + } +} + + + 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..9e98f807ba --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -0,0 +1,41 @@ +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.FeeLayoutState +import com.tangem.tap.features.send.redux.SendState +import kotlinx.android.synthetic.main.btn_expand_collapse.* +import kotlinx.android.synthetic.main.layout_send_network_fee.* +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber(fragment) { + + override fun updateWithNewState(fg: Fragment, state: SendState) { + when (state.lastChangedStateType) { + is FeeLayoutState -> handleFeeLayoutState(fg, state.feeLayoutState) + } + } + + 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 + + (fg.llFeeContainer.parent?.parent as? ViewGroup)?.let { TransitionManager.beginDelayedTransition(it) } + fg.llFeeContainer.visibility = layoutState.visibility + } + + if (fg.swLorem.isChecked != layoutState.swLoremIsChecked) { + fg.swLorem.isChecked = layoutState.swLoremIsChecked + } + + 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 72617cc32a..f0415fea1b 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..17385e89d0 100644 --- a/app/src/main/res/layout/layout_send_network_fee.xml +++ b/app/src/main/res/layout/layout_send_network_fee.xml @@ -13,12 +13,12 @@ android:layout_marginStart="16dp" android:text="@string/send_network_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="Lorem ipsum dolor sit amet, consectetur adipiscing elit. At tristique ornare auctor in." /> - + - - - \ 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 541fb90bac..99d4f806f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,7 @@ Error response while creating PayID. + Send Address or PayID Network fee Low From 44f68718221cb7d38d9ab4730949a04c9025fdf3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Sep 2020 22:16:51 +0300 Subject: [PATCH 2/4] Updated on 2026-08-14 --- .../com/tangem/tap/features/send/redux/SendAction.kt | 2 +- .../tangem/tap/features/send/redux/SendReducer.kt | 4 ++-- .../com/tangem/tap/features/send/redux/SendState.kt | 2 +- .../com/tangem/tap/features/send/ui/SendFragment.kt | 4 ++-- .../send/ui/stateSubscribers/SendStateSubscriber.kt | 4 ++-- app/src/main/res/layout/layout_send_network_fee.xml | 12 ++++++------ app/src/main/res/values/strings.xml | 9 +++++---- 7 files changed, 19 insertions(+), 18 deletions(-) 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 index 6621539b8d..1e28b71713 100644 --- 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 @@ -10,5 +10,5 @@ interface SendAction : Action sealed class FeeLayout : SendAction { object ToggleFeeLayoutVisibility : FeeLayout() data class ChangeSelectedFee(val id: Int) : FeeLayout() - class ChangeLoremState(val isChecked: Boolean) : 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/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt index 7e70ffe625..69c58dde89 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 @@ -31,8 +31,8 @@ class SendReducer { val result = state.copy(selectedFeeId = action.id) updateLastState(sendState.copy(feeLayoutState = result), result) } - is FeeLayout.ChangeLoremState -> { - val result = state.copy(swLoremIsChecked = action.isChecked) + is FeeLayout.ChangeIncludeFee -> { + val result = state.copy(includeFeeIsChecked = action.isChecked) updateLastState(sendState.copy(feeLayoutState = result), result) } } 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 9baf474de8..e2e5c5aa38 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 @@ -17,5 +17,5 @@ class NoneState : StateType data class FeeLayoutState( val visibility: Int = View.GONE, val selectedFeeId: Int = R.id.chipNormal, - val swLoremIsChecked: Boolean = false + 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 index f7880fec0e..40f2689a18 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 @@ -27,8 +27,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { chipGroup.setOnCheckedChangeListener { group, checkedId -> store.dispatch(FeeLayout.ChangeSelectedFee(checkedId)) } - swLorem.setOnCheckedChangeListener { btn, isChecked -> - store.dispatch(FeeLayout.ChangeLoremState(isChecked)) + swIncludeFee.setOnCheckedChangeListener { btn, isChecked -> + store.dispatch(FeeLayout.ChangeIncludeFee(isChecked)) } } 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 9e98f807ba..3f4ea153f4 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 @@ -30,8 +30,8 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber + android:text="@string/send_fee_low" /> + android:text="@string/send_fee_normal" /> + android:text="@string/send_fee_priority" /> + android:text="" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 99d4f806f6..e2e3a516c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,10 +33,11 @@ Send Address or PayID - Network fee - Low - Normal - Priority + Network fee + Include fee + Low + Normal + Priority Send Total Amount From 0802aa6b250743b10db49266d4c932d18541cc97 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Sep 2020 02:55:23 +0300 Subject: [PATCH 3/4] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 2 + .../com/tangem/tap/common/extensions/UI.kt | 8 ++ .../common/qrCodeScan/ScanQrCodeActivity.kt | 65 ++++++++++++++ .../com/tangem/tap/common/redux/AppState.kt | 4 +- .../send => common}/redux/LogMiddleware.kt | 3 +- .../tap/features/send/BaseStoreFragment.kt | 6 ++ .../tap/features/send/redux/SendAction.kt | 14 --- .../tap/features/send/redux/SendMiddleware.kt | 86 +++++++++++++++++++ .../tap/features/send/redux/SendReducer.kt | 42 +++++++-- .../features/send/redux/SendScreenAction.kt | 29 +++++++ .../tap/features/send/redux/SendState.kt | 15 ++++ .../tap/features/send/ui/SendFragment.kt | 21 ++++- .../stateSubscribers/SendStateSubscriber.kt | 9 +- 13 files changed, 271 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt rename app/src/main/java/com/tangem/tap/{features/send => common}/redux/LogMiddleware.kt (75%) delete mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt 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 From 64fa729f84e046852fbcc639b6d2f8fe88242bed Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Sep 2020 13:58:27 +0300 Subject: [PATCH 4/4] Updated on 2026-08-14 --- .../com/tangem/tap/domain/PayIdManager.kt | 4 + .../tap/features/send/BaseStoreFragment.kt | 6 -- .../tap/features/send/redux/SendMiddleware.kt | 3 +- .../tap/features/send/redux/SendReducer.kt | 92 ++++++++++--------- .../features/send/redux/SendScreenAction.kt | 2 +- .../tap/features/send/redux/SendState.kt | 8 +- .../tap/features/send/ui/SendFragment.kt | 6 ++ .../tap/features/wallet/ui/WalletFragment.kt | 2 +- 8 files changed, 62 insertions(+), 61 deletions(-) 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 index c3c591a1c4..20669d7f3b 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,7 +5,6 @@ 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 @@ -43,9 +42,4 @@ 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/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt index 8b9800c188..f734d0816c 100644 --- 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 @@ -2,6 +2,7 @@ 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 @@ -39,7 +40,7 @@ internal class AddressPayIdHandler { val walletManager = store.state.globalState.walletManager ?: return val clipboardData = data ?: return - if (AddressPayIDState.isPayIDAddress(clipboardData)) { + if (PayIdManager.isPayId(clipboardData)) { if (walletManager.wallet.blockchain.isPayIdSupported()) { store.dispatch(AddressPayIdAction.Verification.PayIdNotSupportedByBlockchain) } else { 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 2cf6e9fe78..67a2f55308 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 @@ -12,58 +12,60 @@ import timber.log.Timber */ class SendReducer { companion object { - fun reduce(action: Action, sendState: SendState): SendState { - if (action is SendRelease) return SendState() - val sendAction = action as? SendScreenAction ?: return sendState + fun reduce(action: Action, sendState: SendState): SendState = internalReduce(action, sendState) + } +} - return when (sendAction) { - is AddressPayIdActionUI -> handleAddressPayIdAction(sendAction, sendState, sendState.addressPayIDState) - is FeeActionUI -> handleFeeLayoutAction(sendAction, sendState, sendState.feeLayoutState) - else -> 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) } + } - private fun handleAddressPayIdAction( - action: AddressPayIdActionUI, - sendState: SendState, - state: AddressPayIDState - ): SendState { - var state = state + return sendState +} - when (action) { - is SetAddressOrPayId -> { - state = state.copy(value = action.data?.toString()) - return updateLastState(sendState.copy(addressPayIDState = state), state) - } - } +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 - return sendState + val result = state.copy(visibility = visibility) + updateLastState(sendState.copy(feeLayoutState = result), result) } - - 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) - } - } + is ChangeSelectedFee -> { + val result = state.copy(selectedFeeId = action.id) + 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 + 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 index 3da6e6acce..6e0075913b 100644 --- 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 @@ -8,7 +8,7 @@ import org.rekotlin.Action interface SendScreenAction : Action interface SendScreenActionUI : SendScreenAction -object SendRelease : Action +object ReleaseSendState : Action sealed class FeeActionUI : SendScreenActionUI { object ToggleFeeLayoutVisibility : FeeActionUI() 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 d17bab98c9..70036db005 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 @@ -21,13 +21,7 @@ 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 - } -} +) : StateType data class FeeLayoutState( val visibility: Int = View.GONE, 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 06929486da..1879d895a8 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 @@ -8,6 +8,7 @@ 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 @@ -56,6 +57,11 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { 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/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 9adc6a1030..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 @@ -56,7 +56,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber