Updated on 2026-08-14
This commit is contained in:
parent
64af848bd9
commit
cc468b2d0d
17 changed files with 579 additions and 94 deletions
1
.idea/dictionaries/romanpotapov.xml
generated
1
.idea/dictionaries/romanpotapov.xml
generated
|
|
@ -2,6 +2,7 @@
|
|||
<dictionary name="romanpotapov">
|
||||
<words>
|
||||
<w>blockchain</w>
|
||||
<w>passcode</w>
|
||||
<w>tangem</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package com.tangem.tap.common.entities
|
||||
|
||||
abstract class Button(val enabled: Boolean)
|
||||
open class Button(val enabled: Boolean)
|
||||
|
|
@ -6,6 +6,7 @@ import androidx.fragment.app.FragmentManager
|
|||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.features.details.ui.DetailsConfirmFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsSecurityFragment
|
||||
import com.tangem.tap.features.home.HomeFragment
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
|
|
@ -40,5 +41,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.Send -> SendFragment()
|
||||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,4 +9,4 @@ data class NavigationState(
|
|||
val activity: WeakReference<FragmentActivity>? = null
|
||||
) : StateType
|
||||
|
||||
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm }
|
||||
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm, DetailsSecurity }
|
||||
|
|
@ -2,11 +2,10 @@ package com.tangem.tap.domain
|
|||
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.tangem.*
|
||||
import com.tangem.commands.CommandResponse
|
||||
import com.tangem.commands.PurgeWalletCommand
|
||||
import com.tangem.commands.PurgeWalletResponse
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.CardType
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
|
|
@ -26,12 +25,28 @@ class TangemSdkManager(val activity: ComponentActivity) {
|
|||
return runTaskAsyncReturnOnMain(ScanNoteTask())
|
||||
}
|
||||
|
||||
suspend fun createWallet(): CompletionResult<ScanNoteResponse> {
|
||||
return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask())
|
||||
suspend fun createWallet(cardId: String?): CompletionResult<ScanNoteResponse> {
|
||||
return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId)
|
||||
}
|
||||
|
||||
suspend fun eraseWallet(): CompletionResult<PurgeWalletResponse> {
|
||||
return runTaskAsyncReturnOnMain(PurgeWalletCommand())
|
||||
suspend fun eraseWallet(cardId: String?): CompletionResult<PurgeWalletResponse> {
|
||||
return runTaskAsyncReturnOnMain(PurgeWalletCommand(), cardId)
|
||||
}
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand.setPin1(null), cardId)
|
||||
}
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand.setPin2(null), cardId)
|
||||
}
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand(
|
||||
pinType = PinType.Pin1,
|
||||
newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
|
||||
newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
|
||||
), cardId)
|
||||
}
|
||||
|
||||
private suspend fun <T : CommandResponse> runTaskAsync(
|
||||
|
|
|
|||
|
|
@ -41,4 +41,18 @@ sealed class DetailsAction : Action {
|
|||
data class SelectAppCurrency(val fiatCurrencyName: FiatCurrencyName): AppCurrencyAction()
|
||||
}
|
||||
|
||||
sealed class ManageSecurity : DetailsAction() {
|
||||
object OpenSecurity : ManageSecurity()
|
||||
data class SelectOption(val option: SecurityOption) : ManageSecurity()
|
||||
object SaveChanges : ManageSecurity() {
|
||||
object Success : ManageSecurity()
|
||||
object Failure : ManageSecurity()
|
||||
}
|
||||
data class ConfirmSelection(val option: SecurityOption) : ManageSecurity() {
|
||||
object AlreadySet: ManageSecurity(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_security_option_already_active
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,64 +18,127 @@ import kotlinx.coroutines.withContext
|
|||
import org.rekotlin.Middleware
|
||||
|
||||
class DetailsMiddleware {
|
||||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val appCurrencyMiddleware = AppCurrencyMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.PrepareScreen -> {
|
||||
scope.launch {
|
||||
val loadedCurrencies = preferencesStorage.getFiatCurrencies()
|
||||
if (loadedCurrencies.isNullOrEmpty()) {
|
||||
val response = CoinMarketCapService().getFiatCurrencies()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
preferencesStorage.saveFiatCurrencies(response.data)
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(loadedCurrencies))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
is DetailsAction.EraseWallet.Proceed -> {
|
||||
when (store.state.detailsState.eraseWalletState) {
|
||||
EraseWalletState.Allowed ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
EraseWalletState.NotAllowedByCard ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotAllowedByCard)
|
||||
EraseWalletState.NotEmpty ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotEmpty)
|
||||
}
|
||||
}
|
||||
is DetailsAction.EraseWallet.Cancel -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.EraseWallet.Confirm -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.eraseWallet()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
preferencesStorage.saveAppCurrency(action.fiatCurrencyName)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrencyName))
|
||||
store.dispatch(WalletAction.LoadFiatRate)
|
||||
}
|
||||
is DetailsAction.PrepareScreen -> prepareData()
|
||||
is DetailsAction.EraseWallet -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareData() {
|
||||
scope.launch {
|
||||
val loadedCurrencies = preferencesStorage.getFiatCurrencies()
|
||||
if (loadedCurrencies.isNullOrEmpty()) {
|
||||
val response = CoinMarketCapService().getFiatCurrencies()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
preferencesStorage.saveFiatCurrencies(response.data)
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(loadedCurrencies))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EraseWalletMiddleware() {
|
||||
fun handle(action: DetailsAction.EraseWallet) {
|
||||
when (action) {
|
||||
is DetailsAction.EraseWallet.Proceed -> {
|
||||
when (store.state.detailsState.eraseWalletState) {
|
||||
EraseWalletState.Allowed ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
EraseWalletState.NotAllowedByCard ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotAllowedByCard)
|
||||
EraseWalletState.NotEmpty ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotEmpty)
|
||||
}
|
||||
}
|
||||
is DetailsAction.EraseWallet.Cancel -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.EraseWallet.Confirm -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.eraseWallet(
|
||||
store.state.detailsState.card?.cardId
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppCurrencyMiddleware {
|
||||
fun handle(action: DetailsAction.AppCurrencyAction) {
|
||||
when (action) {
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
preferencesStorage.saveAppCurrency(action.fiatCurrencyName)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrencyName))
|
||||
store.dispatch(WalletAction.LoadFiatRate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ManageSecurityMiddleware {
|
||||
fun handle(action: DetailsAction.ManageSecurity) {
|
||||
when (action) {
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ConfirmSelection -> {
|
||||
if (action.option != store.state.detailsState.securityScreenState?.currentOption) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
} else {
|
||||
store.dispatch(DetailsAction.ManageSecurity.ConfirmSelection.AlreadySet)
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SaveChanges -> {
|
||||
val cardId = store.state.detailsState.card?.cardId
|
||||
scope.launch {
|
||||
val result = when (store.state.detailsState.securityScreenState?.selectedOption) {
|
||||
SecurityOption.LongTap -> tangemSdkManager.setLongTap(cardId)
|
||||
SecurityOption.PassCode -> tangemSdkManager.setPasscode(cardId)
|
||||
SecurityOption.AccessCode -> tangemSdkManager.setAccessCode(cardId)
|
||||
else -> null
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success)
|
||||
}
|
||||
is CompletionResult.Failure, null ->
|
||||
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.commands.Settings
|
|||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
import java.util.*
|
||||
|
||||
class DetailsReducer {
|
||||
companion object {
|
||||
|
|
@ -13,28 +14,45 @@ class DetailsReducer {
|
|||
}
|
||||
|
||||
private fun internalReduce(action: Action, state: AppState): DetailsState {
|
||||
|
||||
if (action !is DetailsAction) return state.detailsState
|
||||
|
||||
var detailsState = state.detailsState
|
||||
when (action) {
|
||||
val detailsState = state.detailsState
|
||||
return when (action) {
|
||||
is DetailsAction.PrepareScreen -> {
|
||||
detailsState = DetailsState(
|
||||
card = action.card, wallet = action.wallet,
|
||||
cardInfo = action.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(
|
||||
action.fiatCurrencyName
|
||||
)
|
||||
)
|
||||
handlePrepareScreen(action, detailsState)
|
||||
}
|
||||
is DetailsAction.EraseWallet -> {
|
||||
detailsState = handleEraseWallet(action, detailsState)
|
||||
handleEraseWallet(action, detailsState)
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction -> {
|
||||
detailsState = handleAppCurrencyAction(action, detailsState)
|
||||
handleAppCurrencyAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ManageSecurity -> {
|
||||
handleSecurityAction(action, detailsState)
|
||||
}
|
||||
}
|
||||
return detailsState
|
||||
}
|
||||
|
||||
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState {
|
||||
val securityOption = when {
|
||||
action.card.isPin1Default == false -> {
|
||||
SecurityOption.AccessCode
|
||||
}
|
||||
action.card.isPin2Default == false -> {
|
||||
SecurityOption.PassCode
|
||||
}
|
||||
else -> {
|
||||
SecurityOption.LongTap
|
||||
}
|
||||
}
|
||||
return DetailsState(
|
||||
card = action.card, wallet = action.wallet,
|
||||
cardInfo = action.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(
|
||||
action.fiatCurrencyName
|
||||
),
|
||||
securityScreenState = SecurityScreenState(currentOption = securityOption)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsState): DetailsState {
|
||||
|
|
@ -88,6 +106,55 @@ private fun handleAppCurrencyAction(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSecurityAction(
|
||||
action: DetailsAction.ManageSecurity, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
val prohibitDefaultPin = state.card?.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true
|
||||
val allowSetPin1 = state.card?.settingsMask?.contains(Settings.AllowSetPIN1) != false
|
||||
val allowSetPin2 = state.card?.settingsMask?.contains(Settings.AllowSetPIN2) != false
|
||||
val isDefaultPin1 = state.card?.isPin1Default != false
|
||||
val isDefaultPin2 = state.card?.isPin2Default != false
|
||||
|
||||
val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java)
|
||||
if ((isDefaultPin1 && isDefaultPin2) || !prohibitDefaultPin) {
|
||||
allowedSecurityOptions.add(SecurityOption.LongTap)
|
||||
}
|
||||
if (allowSetPin1 && (isDefaultPin2 || !prohibitDefaultPin)) {
|
||||
allowedSecurityOptions.add(SecurityOption.AccessCode)
|
||||
}
|
||||
if (allowSetPin2 && (isDefaultPin1 || !prohibitDefaultPin)) {
|
||||
allowedSecurityOptions.add(SecurityOption.AccessCode)
|
||||
}
|
||||
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = allowedSecurityOptions
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SelectOption -> {
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
selectedOption = action.option
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ConfirmSelection -> {
|
||||
val confirmScreenState = when (action.option) {
|
||||
SecurityOption.LongTap -> ConfirmScreenState.LongTap
|
||||
SecurityOption.PassCode -> ConfirmScreenState.PassCode
|
||||
SecurityOption.AccessCode -> ConfirmScreenState.AccessCode
|
||||
}
|
||||
state.copy(confirmScreenState = confirmScreenState)
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SaveChanges.Success -> {
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
currentOption = state.securityScreenState.selectedOption
|
||||
))
|
||||
}
|
||||
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun Card.toCardInfo(): CardInfo? {
|
||||
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
|
||||
val issuer = this.cardData?.issuerName ?: return null
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ package com.tangem.tap.features.details.redux
|
|||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import org.rekotlin.StateType
|
||||
import java.util.*
|
||||
|
||||
data class DetailsState(
|
||||
val card: Card? = null,
|
||||
|
|
@ -14,6 +16,7 @@ data class DetailsState(
|
|||
val appCurrencyState: AppCurrencyState = AppCurrencyState(),
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
val securityScreenState: SecurityScreenState? = null,
|
||||
) : StateType
|
||||
|
||||
data class CardInfo(
|
||||
|
|
@ -24,6 +27,14 @@ data class CardInfo(
|
|||
|
||||
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }
|
||||
enum class ConfirmScreenState { EraseWallet, LongTap, AccessCode, PassCode }
|
||||
data class SecurityScreenState(
|
||||
val currentOption: SecurityOption = SecurityOption.LongTap,
|
||||
val selectedOption: SecurityOption = currentOption,
|
||||
val allowedOptions: EnumSet<SecurityOption> = EnumSet.allOf(SecurityOption::class.java),
|
||||
val buttonProceed: Button = Button(true)
|
||||
)
|
||||
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
data class AppCurrencyState(
|
||||
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -59,16 +59,23 @@ class DetailsConfirmFragment : Fragment(R.layout.fragment_details_confirm),
|
|||
when (state.confirmScreenState) {
|
||||
ConfirmScreenState.EraseWallet -> {
|
||||
toolbar.title = getString(R.string.details_erase_wallet)
|
||||
tv_warning_description.text = getString(R.string.details_erase_wallet_warning)
|
||||
btn_confirm.text = getString(R.string.details_erase_wallet)
|
||||
btn_confirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null, getDrawable(R.drawable.ic_send), null
|
||||
)
|
||||
btn_confirm.setOnClickListener { store.dispatch(DetailsAction.EraseWallet.Confirm) }
|
||||
}
|
||||
ConfirmScreenState.LongTap -> TODO()
|
||||
ConfirmScreenState.AccessCode -> TODO()
|
||||
ConfirmScreenState.PassCode -> TODO()
|
||||
null -> TODO()
|
||||
ConfirmScreenState.LongTap, ConfirmScreenState.AccessCode,
|
||||
ConfirmScreenState.PassCode -> {
|
||||
toolbar.title = getString(R.string.details_manage_security)
|
||||
tv_warning_description.text = getString(R.string.details_manage_security_warning)
|
||||
btn_confirm.text = getString(R.string.details_save_changes)
|
||||
btn_confirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null, getDrawable(R.drawable.ic_save), null
|
||||
)
|
||||
btn_confirm.setOnClickListener { store.dispatch(DetailsAction.ManageSecurity.SaveChanges) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.transition.TransitionInflater
|
|||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_details.*
|
||||
|
|
@ -74,6 +75,18 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
store.dispatch(DetailsAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
|
||||
tv_security_title.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
}
|
||||
|
||||
val currentSecurity = when (state.securityScreenState?.currentOption) {
|
||||
SecurityOption.LongTap -> R.string.details_manage_security_long_tap
|
||||
SecurityOption.PassCode -> R.string.details_manage_security_passcode
|
||||
SecurityOption.AccessCode -> R.string.details_manage_security_access_code
|
||||
null -> null
|
||||
}
|
||||
currentSecurity?.let { tv_security.text = getString(it) }
|
||||
|
||||
if (state.appCurrencyState.showAppCurrencyDialog &&
|
||||
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
|
||||
currencySelectionDialog.show(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.tap.features.details.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_details_confirm.toolbar
|
||||
import kotlinx.android.synthetic.main.fragment_details_security.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
setOnClickListeners()
|
||||
}
|
||||
|
||||
private fun setOnClickListeners() {
|
||||
v_long_tap.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.LongTap))
|
||||
}
|
||||
v_passcode.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.PassCode))
|
||||
}
|
||||
v_access_code.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.AccessCode))
|
||||
}
|
||||
btn_save_changes.setOnClickListener {
|
||||
store.state.detailsState.securityScreenState?.selectedOption?.let {
|
||||
store.dispatch(DetailsAction.ManageSecurity.ConfirmSelection(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null) return
|
||||
selectSecurityOption(state.securityScreenState?.selectedOption)
|
||||
}
|
||||
|
||||
private fun selectSecurityOption(securityOption: SecurityOption?) {
|
||||
radiobutton_long_tap.isChecked = securityOption == SecurityOption.LongTap
|
||||
radiobutton_passcode.isChecked = securityOption == SecurityOption.PassCode
|
||||
radiobutton_access_code.isChecked = securityOption == SecurityOption.AccessCode
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,9 @@ val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet()
|
||||
val result = tangemSdkManager.createWallet(
|
||||
store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(result.data)
|
||||
|
|
|
|||
9
app/src/main/res/drawable/ic_save.xml
Normal file
9
app/src/main/res/drawable/ic_save.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M20.7825,6.9675L17.0325,3.2175C16.9624,3.148 16.8793,3.093 16.7879,3.0557C16.6966,3.0184 16.5987,2.9994 16.5,3H4.5C4.1022,3 3.7206,3.1581 3.4393,3.4393C3.158,3.7207 3,4.1022 3,4.5V19.5C3,19.8978 3.158,20.2794 3.4393,20.5607C3.7206,20.842 4.1022,21 4.5,21H19.5C19.8978,21 20.2794,20.842 20.5607,20.5607C20.842,20.2794 21,19.8978 21,19.5V7.5C21.0006,7.4013 20.9817,7.3035 20.9443,7.2121C20.907,7.1207 20.852,7.0376 20.7825,6.9675ZM9,4.5H15V7.5H9V4.5ZM15,19.5H9V13.5H15V19.5ZM16.5,19.5V13.5C16.5,13.1022 16.342,12.7207 16.0607,12.4394C15.7794,12.158 15.3978,12 15,12H9C8.6022,12 8.2206,12.158 7.9393,12.4394C7.658,12.7207 7.5,13.1022 7.5,13.5V19.5H4.5V4.5H7.5V7.5C7.5,7.8978 7.658,8.2794 7.9393,8.5607C8.2206,8.842 8.6022,9 9,9H15C15.3978,9 15.7794,8.842 16.0607,8.5607C16.342,8.2794 16.5,7.8978 16.5,7.5V4.8075L19.5,7.8075V19.5H16.5Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -68,34 +68,27 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<Space
|
||||
android:id="@+id/space"
|
||||
android:layout_width="93dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginTop="30dp"
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_confirm"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_confirm"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="200dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
tools:text="@string/wallet_button_send"
|
||||
tools:icon="@drawable/ic_send"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/space"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
197
app/src/main/res/layout/fragment_details_security.xml
Normal file
197
app/src/main/res/layout/fragment_details_security.xml
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/coordinator_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:fitsSystemWindows="true"
|
||||
app:liftOnScroll="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/details_manage_security"
|
||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_24" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_long_tap_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:text="@string/details_manage_security_long_tap"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/radiobutton_long_tap"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_long_tap_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="60dp"
|
||||
android:textColor="@color/darkGray1"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_long_tap_title"
|
||||
android:text="Caution: When having the switch on, all data will be encrypted and kept from being stolen."
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/v_long_tap"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_long_tap_title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_long_tap_description"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_passcode_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_long_tap"
|
||||
android:text="@string/details_manage_security_passcode"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/radiobutton_passcode"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/v_passcode"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_passcode_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="60dp"
|
||||
android:textColor="@color/darkGray1"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_passcode_title"
|
||||
android:text="Caution: When having the switch on, all data will be encrypted and kept from being stolen."
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/v_passcode"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_passcode_title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_passcode_description"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_access_code_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_passcode"
|
||||
android:text="@string/details_manage_security_access_code"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/radiobutton_access_code"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/v_access_code"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_access_code_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="60dp"
|
||||
android:textColor="@color/darkGray1"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_access_code_title"
|
||||
android:text="Caution: When having the switch on, all data will be encrypted and kept from being stolen."
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/v_access_code"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_access_code_title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_access_code_description"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_save_changes"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:text="@string/details_save_changes"
|
||||
app:icon="@drawable/ic_save"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -86,6 +86,7 @@
|
|||
<string name="details_toolbar_title">Details</string>
|
||||
<string name="details_notification_erase_wallet_not_allowed">Card settings prohibits from erasing wallet</string>
|
||||
<string name="details_notification_erase_wallet_not_possible">You balance on this wallet is not zero, or you have unconfirmed transactions</string>
|
||||
<string name="details_notification_security_option_already_active">This is the currently active option</string>
|
||||
<string name="details_card_id">Card ID</string>
|
||||
<string name="details_issuer">Issuer</string>
|
||||
<string name="details_signed_hashes">Signed</string>
|
||||
|
|
@ -95,6 +96,13 @@
|
|||
<string name="details_validate_card">Validate card</string>
|
||||
<string name="details_manage_security">Manage security</string>
|
||||
<string name="details_erase_wallet">Erase wallet</string>
|
||||
<string name="details_save_changes">Save changes</string>
|
||||
<string name="details_manage_security_long_tap">Long Tap</string>
|
||||
<string name="details_manage_security_passcode">Passcode</string>
|
||||
<string name="details_manage_security_access_code">Access Code</string>
|
||||
<string name="details_erase_wallet_warning">This action is irreversible. If, after deleting the wallet, someone sends funds to it, then you will not be able to withdraw them.</string>
|
||||
<string name="details_manage_security_warning">If you forget the code, you will lose the ability to use the card. There is no way to recover or change your code if you lose it.
|
||||
</string>
|
||||
|
||||
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue