From 0bb2dff5f1d263d60809b8ebec4c05efd8887a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Wed, 2 Oct 2024 18:29:43 +0200 Subject: [PATCH 01/32] Refactor wallet handling (#1215) --- .../wallet/service/TariWalletService.aidl | 28 -- .../service/TariWalletServiceListener.aidl | 67 --- .../application/baseNodes/BaseNodesManager.kt | 75 +--- .../deeplinks/DeeplinkViewModel.kt | 1 + .../walletManager/WalletManager.kt | 400 ++++++++++++++++-- .../walletManager/WalletStateHandler.kt | 81 ++-- .../baseNode/BaseNodePrefRepository.kt | 11 - .../com/tari/android/wallet/event/Event.kt | 19 +- .../com/tari/android/wallet/ffi/FFIWallet.kt | 370 +++++++--------- .../android/wallet/ffi/FFIWalletListener.kt | 2 +- .../backup/BackupFileProcessor.kt | 6 +- .../infrastructure/backup/BackupManager.kt | 39 +- .../infrastructure/backup/BackupUtxos.kt | 2 +- .../infrastructure/logging/FFIFileAdapter.kt | 4 +- .../infrastructure/logging/LoggerAdapter.kt | 9 +- .../tari/android/wallet/model/TariContact.kt | 4 - .../wallet/model/seedPhrase/SeedPhrase.kt | 13 +- .../service/service/BaseNodeValidationType.kt | 6 - .../service/service/FFIWalletListenerImpl.kt | 343 --------------- .../service/TariWalletServiceStubImpl.kt | 60 +-- .../service/TariWalletServiceStubProxy.kt | 17 - .../wallet/service/service/WalletService.kt | 36 +- .../wallet/ui/common/CommonViewModel.kt | 23 +- .../contactBook/data/ContactsRepository.kt | 6 +- .../data/FFIContactsRepositoryBridge.kt | 42 +- .../fragment/home/navigation/TariNavigator.kt | 5 +- .../activity/OnboardingFlowActivity.kt | 7 +- .../createWallet/CreateWalletFragment.kt | 5 - .../createWallet/CreateWalletViewModel.kt | 12 +- .../localAuth/LocalAuthViewModel.kt | 11 +- .../ChooseRestoreOptionViewModel.kt | 15 +- .../EnterRestorationPasswordViewModel.kt | 21 +- .../inputSeedWords/InputSeedWordsFragment.kt | 2 +- .../inputSeedWords/InputSeedWordsViewModel.kt | 32 +- .../WalletRestoringFromSeedWordsViewModel.kt | 8 +- .../send/addAmount/AddAmountViewModel.kt | 11 +- .../send/finalize/FinalizeSendTxViewModel.kt | 14 +- .../changeBaseNode/ChangeBaseNodeViewModel.kt | 2 + .../NetworkSelectionViewModel.kt | 8 +- .../TorBridgesSelectionViewModel.kt | 8 +- .../ui/fragment/splash/SplashActivity.kt | 7 +- .../ui/fragment/tx/HomeFragmentViewModel.kt | 37 +- .../ui/fragment/tx/TransactionRepository.kt | 34 +- .../fragment/tx/details/TxDetailsViewModel.kt | 31 +- .../com/tari/android/wallet/util/Constants.kt | 2 - 45 files changed, 818 insertions(+), 1118 deletions(-) delete mode 100644 app/src/main/aidl/com/tari/android/wallet/service/TariWalletServiceListener.aidl delete mode 100644 app/src/main/java/com/tari/android/wallet/service/service/BaseNodeValidationType.kt delete mode 100644 app/src/main/java/com/tari/android/wallet/service/service/FFIWalletListenerImpl.kt diff --git a/app/src/main/aidl/com/tari/android/wallet/service/TariWalletService.aidl b/app/src/main/aidl/com/tari/android/wallet/service/TariWalletService.aidl index d5c2358d8..45b9d2faa 100644 --- a/app/src/main/aidl/com/tari/android/wallet/service/TariWalletService.aidl +++ b/app/src/main/aidl/com/tari/android/wallet/service/TariWalletService.aidl @@ -34,23 +34,9 @@ package com.tari.android.wallet.service; // import model classes import com.tari.android.wallet.model.Model; -import com.tari.android.wallet.service.TariWalletServiceListener; interface TariWalletService { - /** - * Registers new wallet listener. - * Registered listener will be unregistered on death. - */ - boolean registerListener(TariWalletServiceListener listener); - - /** - * Unregisters wallet listener. - */ - boolean unregisterListener(TariWalletServiceListener listener); - - String getWalletAddressBase58(out WalletError error); - BalanceInfo getBalanceInfo(out WalletError error); MicroTari estimateTxFee(in MicroTari amount, out WalletError error, in MicroTari feePerGram); @@ -71,17 +57,6 @@ interface TariWalletService { boolean cancelPendingTx(in TxId id, out WalletError error); - /** - * Sets the base node peer that the wallet syncs with. - */ - boolean addBaseNodePeer( - in String baseNodePublicKey, - in String baseNodeAddress, - out WalletError error - ); - - boolean startBaseNodeSync(out WalletError error); - TxId sendTari( in TariContact contact, in MicroTari amount, @@ -96,9 +71,6 @@ interface TariWalletService { boolean removeContact(in TariWalletAddress address, out WalletError error); - TariWalletAddress getWalletAddressFromEmojiId(in String emojiId, out WalletError error); - TariWalletAddress getWalletAddressFromBase58(in String base58, out WalletError error); - /** * Key-value storage functions. */ diff --git a/app/src/main/aidl/com/tari/android/wallet/service/TariWalletServiceListener.aidl b/app/src/main/aidl/com/tari/android/wallet/service/TariWalletServiceListener.aidl deleted file mode 100644 index 888e42189..000000000 --- a/app/src/main/aidl/com/tari/android/wallet/service/TariWalletServiceListener.aidl +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020 The Tari Project - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the - * following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of - * its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.tari.android.wallet.service; - -// import model classes -import com.tari.android.wallet.model.Model; - -// Declare any non-default types here with import statements - -interface TariWalletServiceListener { - - oneway void onTxReceived(in PendingInboundTx pendingInboundTx); - - oneway void onTxReplyReceived(in PendingOutboundTx pendingOutboundTx); - - oneway void onTxFinalized(in PendingInboundTx pendingInboundTx); - - oneway void onInboundTxBroadcast(in PendingInboundTx pendingInboundTx); - - oneway void onOutboundTxBroadcast(in PendingOutboundTx pendingOutboundTx); - - oneway void onTxMined(in CompletedTx completed); - - oneway void onTxMinedUnconfirmed(in CompletedTx completed, in int confirmationCount); - - oneway void onTxFauxConfirmed(in CompletedTx completed); - - oneway void onTxFauxUnconfirmed(in CompletedTx completed, in int confirmationCount); - - oneway void onTxCancelled(in CancelledTx tx); - - oneway void onDirectSendResult(in TxId txId, in TransactionSendStatus status); - - oneway void onBalanceUpdated(in BalanceInfo balanceInfo); - - oneway void onBaseNodeSyncComplete(in boolean success); -} diff --git a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt index 5b908bd11..a67df8654 100644 --- a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt @@ -1,29 +1,20 @@ package com.tari.android.wallet.application.baseNodes import android.content.Context -import com.google.gson.Gson import com.orhanobut.logger.Logger import com.tari.android.wallet.R import com.tari.android.wallet.application.Network -import com.tari.android.wallet.application.walletManager.WalletStateHandler import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeList import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository -import com.tari.android.wallet.di.ApplicationScope -import com.tari.android.wallet.extension.getWithError -import com.tari.android.wallet.ffi.FFIPublicKey import com.tari.android.wallet.ffi.FFITariBaseNodeState import com.tari.android.wallet.ffi.FFIWallet -import com.tari.android.wallet.ffi.HexString -import com.tari.android.wallet.service.connection.TariWalletServiceConnection +import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.util.DebugConfig -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch import org.apache.commons.io.IOUtils import java.math.BigInteger import javax.inject.Inject @@ -39,9 +30,6 @@ class BaseNodesManager @Inject constructor( private val context: Context, private val baseNodeSharedRepository: BaseNodePrefRepository, private val networkRepository: NetworkPrefRepository, - private val serviceConnection: TariWalletServiceConnection, - private val walletStateHandler: WalletStateHandler, - @ApplicationScope private val applicationScope: CoroutineScope, ) { private val logger get() = Logger.t(this::class.simpleName) @@ -63,6 +51,12 @@ class BaseNodesManager @Inject constructor( private val _networkBlockHeight = MutableStateFlow(BigInteger.ZERO) val networkBlockHeight = _networkBlockHeight.asStateFlow() + val currentBaseNode: BaseNodeDto? + get() = baseNodeSharedRepository.currentBaseNode + + val userBaseNodes: List + get() = baseNodeSharedRepository.userBaseNodes + /** * Select a base node randomly from the list of base nodes in base_nodes.tx, and sets * the wallet and stored the values in shared prefs. @@ -73,16 +67,17 @@ class BaseNodesManager @Inject constructor( val currentBaseNode = baseNodeSharedRepository.currentBaseNode ?: baseNodeList.firstOrNull() val nextBaseNode = baseNodeList.getOrNull(baseNodeList.indexOf(currentBaseNode) + 1) - baseNodeSharedRepository.baseNodeLastSyncResult = null baseNodeSharedRepository.currentBaseNode = nextBaseNode return nextBaseNode } + /** + * Sets the base node to the given base node. + * Need to call WalletManager.syncBaseNode() after this method. + */ fun setBaseNode(baseNode: BaseNodeDto) { - baseNodeSharedRepository.baseNodeLastSyncResult = null baseNodeSharedRepository.currentBaseNode = baseNode - startSync() } fun addUserBaseNode(baseNode: BaseNodeDto) { @@ -107,40 +102,6 @@ class BaseNodesManager @Inject constructor( _walletScannedHeight.update { height } } - fun startSync() { - //essential for wallet creation flow - var currentBaseNode: BaseNodeDto? = baseNodeSharedRepository.currentBaseNode ?: return - - applicationScope.launch(Dispatchers.IO) { - serviceConnection.doOnWalletServiceConnected { walletService -> - walletStateHandler.doOnWalletRunning { ffiWallet -> - while (currentBaseNode != null) { - try { - currentBaseNode?.let { - logger.i("startSync") - logger.i("startSync:publicKeyHex: ${it.publicKeyHex}") - logger.i("startSync:address: ${it.address}") - logger.i("startSync:userBaseNodes: ${Gson().toJson(baseNodeSharedRepository.userBaseNodes)}") - val baseNodeKeyFFI = FFIPublicKey(HexString(it.publicKeyHex)) - ffiWallet.addBaseNodePeer(baseNodeKeyFFI, it.address) - baseNodeKeyFFI.destroy() - walletService.getWithError { error, wallet -> wallet.startBaseNodeSync(error) } - } - break - } catch (e: Throwable) { - logger.i("startSync:error connecting to base node $currentBaseNode with an error: ${e.message}") - currentBaseNode = setNextBaseNode() - } - } - - if (currentBaseNode == null) { - logger.e("startSync: cannot connect to any base node") - } - } - } - } - } - /** * address should be in the format of hex::/onion3/{public_key} or hex::/ip4/{ip}/tcp/{port} */ @@ -148,17 +109,21 @@ class BaseNodesManager @Inject constructor( return Regex(REGEX_ONION).matches(address) || Regex(REGEX_IPV4).matches(address) } - fun refreshBaseNodeList() { - baseNodeSharedRepository.ffiBaseNodes = loadBaseNodesFromFFI() + fun refreshBaseNodeList(wallet: FFIWallet) { + baseNodeSharedRepository.ffiBaseNodes = loadBaseNodesFromFFI(wallet) } - private fun loadBaseNodesFromFFI(): BaseNodeList = FFIWallet.instance?.getBaseNodePeers() - ?.mapIndexed { index, publicKey -> + fun setBaseNodeState(state: BaseNodeState) { + baseNodeSharedRepository.baseNodeState = state + } + + private fun loadBaseNodesFromFFI(wallet: FFIWallet): BaseNodeList = wallet.getBaseNodePeers() + .mapIndexed { index, publicKey -> BaseNodeDto( name = "${networkRepository.currentNetwork.network.displayName} ${index + 1}", publicKeyHex = publicKey.hex, ) - }.orEmpty() + } .let { BaseNodeList(it) } .also { list -> logger.i("baseNodeList from FFI: \n${list.joinToString(separator = "\n")}") } diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt index f0704132f..2e0ef35fd 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt @@ -142,5 +142,6 @@ class DeeplinkViewModel : CommonViewModel() { private fun addBaseNodeAction(baseNodeDto: BaseNodeDto, isQrData: Boolean) { baseNodesManager.addUserBaseNode(baseNodeDto) baseNodesManager.setBaseNode(baseNodeDto) + walletManager.syncBaseNode() } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index 52c7f5cf9..1f1017ccb 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -32,36 +32,73 @@ */ package com.tari.android.wallet.application.walletManager +import com.google.gson.Gson import com.orhanobut.logger.Logger import com.tari.android.wallet.BuildConfig import com.tari.android.wallet.application.Network +import com.tari.android.wallet.application.TariWalletApplication import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository +import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.security.SecurityPrefRepository import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository import com.tari.android.wallet.di.ApplicationScope +import com.tari.android.wallet.event.EffectChannelFlow +import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.safeCastTo import com.tari.android.wallet.ffi.FFIByteVector import com.tari.android.wallet.ffi.FFICommsConfig import com.tari.android.wallet.ffi.FFIException +import com.tari.android.wallet.ffi.FFIPublicKey +import com.tari.android.wallet.ffi.FFITariBaseNodeState import com.tari.android.wallet.ffi.FFITariTransportConfig import com.tari.android.wallet.ffi.FFIWallet +import com.tari.android.wallet.ffi.FFIWalletListener +import com.tari.android.wallet.ffi.HexString import com.tari.android.wallet.ffi.LogFileObserver import com.tari.android.wallet.ffi.NetAddressString +import com.tari.android.wallet.ffi.TransactionValidationStatus +import com.tari.android.wallet.model.BalanceInfo +import com.tari.android.wallet.model.CancelledTx +import com.tari.android.wallet.model.CompletedTx +import com.tari.android.wallet.model.PendingInboundTx +import com.tari.android.wallet.model.PendingOutboundTx +import com.tari.android.wallet.model.TransactionSendStatus +import com.tari.android.wallet.model.Tx +import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.fullBase58 +import com.tari.android.wallet.model.recovery.WalletRestorationResult +import com.tari.android.wallet.notification.NotificationHelper +import com.tari.android.wallet.service.baseNode.BaseNodeState +import com.tari.android.wallet.service.baseNode.BaseNodeSyncState +import com.tari.android.wallet.service.notification.NotificationService import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import com.tari.android.wallet.service.service.WalletService import com.tari.android.wallet.tor.TorConfig import com.tari.android.wallet.tor.TorProxyManager import com.tari.android.wallet.tor.TorProxyStateHandler +import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.util.Constants import com.tari.android.wallet.util.WalletUtil +import io.reactivex.Observable +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import java.io.File +import java.math.BigInteger +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap +import java.util.concurrent.CopyOnWriteArraySet +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import javax.inject.Singleton @@ -76,25 +113,52 @@ class WalletManager @Inject constructor( private val walletConfig: WalletConfig, private val torManager: TorProxyManager, private val corePrefRepository: CorePrefRepository, - private val baseNodePrefRepository: BaseNodePrefRepository, private val seedPhraseRepository: SeedPhraseRepository, private val networkPrefRepository: NetworkPrefRepository, private val tariSettingsPrefRepository: TariSettingsPrefRepository, private val securityPrefRepository: SecurityPrefRepository, private val baseNodesManager: BaseNodesManager, private val torConfig: TorConfig, - private val walletStateHandler: WalletStateHandler, private val torProxyStateHandler: TorProxyStateHandler, + private val app: TariWalletApplication, + private val notificationHelper: NotificationHelper, + private val notificationService: NotificationService, @ApplicationScope private val applicationScope: CoroutineScope, -) { +) : OutboundTxNotifier { + + private var atomicInstance = AtomicReference() + var walletInstance: FFIWallet? + get() = atomicInstance.get() + set(value) = atomicInstance.set(value) + + private val _walletState = MutableStateFlow(WalletState.NotReady) + val walletState = _walletState.asStateFlow() + + private val _walletEvent = EffectChannelFlow() + val walletEvent: Flow = _walletEvent.flow private var logFileObserver: LogFileObserver? = null private val logger get() = Logger.t(WalletManager::class.simpleName) /** - * Start tor and init wallet. + * Maps the validation type to the request id and validation result. This map will be + * initialized at the beginning of each base node validation sequence. + * Validation results will all be null, and will be set as the result callbacks get called. + */ + private val walletValidationStatusMap: ConcurrentMap = ConcurrentHashMap() + + override val outboundTxIdsToBePushNotified = CopyOnWriteArraySet() + + /** + * Debounce for inbound transaction notification. + * TODO don't use rx. Replace with coroutines. */ + private var txReceivedNotificationDelayedAction: Disposable? = null + private var inboundTxEventNotificationTxs = mutableListOf() + + private var txBroadcastRestarted = false + @Synchronized fun start() { torManager.run() @@ -106,27 +170,20 @@ class WalletManager @Inject constructor( } } - /** - * DeInit the wallet and shutdown Tor. - */ @Synchronized fun stop() { // destroy FFI wallet object - FFIWallet.instance?.destroy() - FFIWallet.instance = null - walletStateHandler.setWalletState(WalletState.NotReady) + walletInstance?.destroy() + walletInstance = null + _walletState.update { WalletState.NotReady } // stop tor proxy torManager.shutdown() - walletStateHandler.setWalletState(WalletState.NotReady) // todo Don't understand why it's twice } fun onWalletStarted() { - walletStateHandler.setWalletState(WalletState.Running) + _walletState.update { WalletState.Running } } - /** - * Instantiates the comms configuration for the wallet. - */ fun getCommsConfig(): FFICommsConfig = FFICommsConfig( publicAddress = NetAddressString(address = "127.0.0.1", port = 39069).toString(), transport = getTorTransport(), @@ -136,31 +193,75 @@ class WalletManager @Inject constructor( safMessageDurationSec = Constants.Wallet.STORE_AND_FORWARD_MESSAGE_DURATION_SEC, ) + /** + * Syncs the wallet with the base node and validates the wallet + */ + fun syncBaseNode() { + var currentBaseNode: BaseNodeDto? = baseNodesManager.currentBaseNode ?: return + + applicationScope.launch(Dispatchers.IO) { + doOnWalletRunning { wallet -> + while (currentBaseNode != null) { + try { + currentBaseNode?.let { + logger.i( + "startSync:publicKeyHex: ${it.publicKeyHex}\n" + + "startSync:address: ${it.address}\n" + + "startSync:userBaseNodes: ${Gson().toJson(baseNodesManager.userBaseNodes)}" + ) + + val baseNodeKeyFFI = FFIPublicKey(HexString(it.publicKeyHex)) + val addBaseNodeResult = wallet.addBaseNodePeer(baseNodeKeyFFI, it.address) + baseNodeKeyFFI.destroy() + logger.i("startSync:addBaseNodePeer ${if (addBaseNodeResult) "success" else "failed"}") + + try { + logger.i("startSync:wallet validation:start Tx and TXO validation") + walletValidationStatusMap.clear() + walletValidationStatusMap[WalletValidationType.TXO] = WalletValidationResult(wallet.startTXOValidation(), null) + walletValidationStatusMap[WalletValidationType.TX] = WalletValidationResult(wallet.startTxValidation(), null) + } catch (e: Throwable) { + logger.i("startSync:wallet validation:error: ${e.message}") + walletValidationStatusMap.clear() + EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) + } + } + break + } catch (e: Throwable) { + logger.i("startSync:error connecting to base node $currentBaseNode with an error: ${e.message}") + currentBaseNode = baseNodesManager.setNextBaseNode() + } + } + + if (currentBaseNode == null) { + logger.e("startSync: cannot connect to any base node") + } + } + } + } + private fun startWallet() { - if (walletStateHandler.walletState.value is WalletState.NotReady || walletStateHandler.walletState.value is WalletState.Failed) { + if (walletState.value is WalletState.NotReady || walletState.value is WalletState.Failed) { logger.i("Initialize wallet started") - walletStateHandler.setWalletState(WalletState.Initializing) + _walletState.update { WalletState.Initializing } applicationScope.launch { try { initWallet() - walletStateHandler.setWalletState(WalletState.Started) + _walletState.update { WalletState.Started } logger.i("Wallet was started") } catch (e: Exception) { - val oldCode = walletStateHandler.walletState.value.errorCode + val oldCode = walletState.value.errorCode val newCode = e.safeCastTo()?.error?.code if (oldCode == null || oldCode != newCode) { logger.e(e, "Wallet was failed") } - walletStateHandler.setWalletState(WalletState.Failed(e)) + _walletState.update { WalletState.Failed(e) } } }.start() } } - /** - * Instantiates the Tor transport for the wallet. - */ private fun getTorTransport(): FFITariTransportConfig { val cookieFile = File(torConfig.cookieFilePath) if (!cookieFile.exists()) { @@ -194,7 +295,7 @@ class WalletManager @Inject constructor( */ private fun saveWalletAddressToSharedPrefs() { // set shared preferences values after instantiation - FFIWallet.instance?.getWalletAddress()?.let { ffiTariWalletAddress -> + walletInstance?.getWalletAddress()?.let { ffiTariWalletAddress -> corePrefRepository.walletAddressBase58 = ffiTariWalletAddress.fullBase58() corePrefRepository.emojiId = ffiTariWalletAddress.getEmojiId() ffiTariWalletAddress.destroy() @@ -205,41 +306,268 @@ class WalletManager @Inject constructor( * Initializes the wallet and sets the singleton instance in the wallet companion object. */ private fun initWallet() { - if (FFIWallet.instance == null) { + if (walletInstance == null) { // store network info in shared preferences if it's a new wallet val isNewInstallation = !WalletUtil.walletExists(walletConfig) - val wallet = FFIWallet( + walletInstance = FFIWallet( sharedPrefsRepository = corePrefRepository, securityPrefRepository = securityPrefRepository, seedPhraseRepository = seedPhraseRepository, networkRepository = networkPrefRepository, commsConfig = getCommsConfig(), logPath = walletConfig.getWalletLogFilePath(), + listener = object : FFIWalletListener { + /** + * All the callbacks are called on the FFI thread, so we need to switch to the main thread. + * The app will crash if we try to update the UI from the FFI thread. + */ + override fun onTxReceived(pendingInboundTx: PendingInboundTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxReceived(pendingInboundTx)) + postTxNotification(pendingInboundTx) + } + + override fun onTxReplyReceived(pendingOutboundTx: PendingOutboundTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxReplyReceived(pendingOutboundTx)) + } + + override fun onTxFinalized(pendingInboundTx: PendingInboundTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxFinalized(pendingInboundTx)) + } + + override fun onInboundTxBroadcast(pendingInboundTx: PendingInboundTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.InboundTxBroadcast(pendingInboundTx)) + } + + override fun onOutboundTxBroadcast(pendingOutboundTx: PendingOutboundTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.OutboundTxBroadcast(pendingOutboundTx)) + } + + override fun onTxMined(completedTx: CompletedTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxMined(completedTx)) + } + + override fun onTxMinedUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxMinedUnconfirmed(completedTx, confirmationCount)) + } + + override fun onTxFauxConfirmed(completedTx: CompletedTx) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxFauxConfirmed(completedTx)) + } + + override fun onTxFauxUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxFauxMinedUnconfirmed(completedTx, confirmationCount)) + } + + override fun onDirectSendResult(txId: BigInteger, status: TransactionSendStatus) = runOnMain { + _walletEvent.send(WalletEvent.Tx.DirectSendResult(TxId(txId), status)) + outboundTxIdsToBePushNotified.firstOrNull { it.txId == txId }?.let { + outboundTxIdsToBePushNotified.remove(it) + sendPushNotificationToTxRecipient(it.recipientPublicKeyHex) + } + } + + override fun onTxCancelled(cancelledTx: CancelledTx, rejectionReason: Int) = runOnMain { + _walletEvent.send(WalletEvent.Tx.TxCancelled(cancelledTx)) + + // TODO don't use android components in this class + val currentActivity = app.currentActivity + if (cancelledTx.direction == Tx.Direction.INBOUND + && !(app.isInForeground && currentActivity is HomeActivity && currentActivity.willNotifyAboutNewTx()) + ) { + notificationHelper.postTxCanceledNotification(cancelledTx) + } + } + + override fun onTXOValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) = runOnMain { + checkValidationResult( + type = WalletValidationType.TXO, + responseId = responseId, + isSuccess = status == TransactionValidationStatus.Success, + ) + } + + override fun onTxValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) = runOnMain { + checkValidationResult( + type = WalletValidationType.TX, + responseId = responseId, + isSuccess = status == TransactionValidationStatus.Success, + ) + walletInstance + ?.takeIf { !txBroadcastRestarted && status == TransactionValidationStatus.Success } + ?.let { wallet -> + wallet.restartTxBroadcast() + txBroadcastRestarted = true + } ?: logger.i("Transaction broadcast restart failed because wallet instance is null or tx broadcast already restarted") + } + + override fun onBalanceUpdated(balanceInfo: BalanceInfo) = runOnMain { + EventBus.balanceState.post(balanceInfo) // TODO replace with flow!!! + } + + override fun onConnectivityStatus(status: Int) = runOnMain { + when (ConnectivityStatus.entries[status]) { + ConnectivityStatus.CONNECTING -> { + /* do nothing */ + } + + ConnectivityStatus.ONLINE -> { + walletInstance?.let { baseNodesManager.refreshBaseNodeList(it) } + ?: logger.i("Wallet instance is null when trying to refresh base node list") + baseNodesManager.setBaseNodeState(BaseNodeState.Online) + EventBus.baseNodeState.post(BaseNodeState.Online) + } + + ConnectivityStatus.OFFLINE -> { + val currentBaseNode = baseNodesManager.currentBaseNode + if (currentBaseNode == null || !currentBaseNode.isCustom) { + baseNodesManager.setNextBaseNode() + syncBaseNode() + } + baseNodesManager.setBaseNodeState(BaseNodeState.Offline) + EventBus.baseNodeState.post(BaseNodeState.Offline) + } + } + } + + override fun onWalletRestoration(result: WalletRestorationResult) = runOnMain { + EventBus.walletRestorationState.post(result) // TODO replace with flow!!! + } + + override fun onWalletScannedHeight(height: Int) = runOnMain { + baseNodesManager.saveWalletScannedHeight(height) + } + + override fun onBaseNodeStateChanged(baseNodeState: FFITariBaseNodeState) = runOnMain { + baseNodesManager.saveBaseNodeState(baseNodeState) + } + } ) - FFIWallet.instance = wallet + if (isNewInstallation) { - FFIWallet.instance?.setKeyValue( + walletInstance?.setKeyValue( key = WalletService.Companion.KeyValueStorageKeys.NETWORK, value = networkPrefRepository.currentNetwork.network.uriComponent, ) } else if (tariSettingsPrefRepository.isRestoredWallet && networkPrefRepository.ffiNetwork == null) { networkPrefRepository.ffiNetwork = try { - Network.from(FFIWallet.instance?.getKeyValue(WalletService.Companion.KeyValueStorageKeys.NETWORK) ?: "") + Network.from(walletInstance?.getKeyValue(WalletService.Companion.KeyValueStorageKeys.NETWORK) ?: "") } catch (exception: Exception) { null } } startLogFileObserver() - baseNodesManager.refreshBaseNodeList() - val currentBaseNode = baseNodePrefRepository.currentBaseNode - if (currentBaseNode != null) { - baseNodesManager.startSync() - } else { + walletInstance?.let { baseNodesManager.refreshBaseNodeList(it) } + ?: error("Wallet instance is null when trying to refresh base node list") + if (baseNodesManager.currentBaseNode == null) { baseNodesManager.setNextBaseNode() - baseNodesManager.startSync() } + syncBaseNode() saveWalletAddressToSharedPrefs() } } + + private fun postTxNotification(tx: Tx) { + txReceivedNotificationDelayedAction?.dispose() + inboundTxEventNotificationTxs.add(tx) + txReceivedNotificationDelayedAction = + Observable.timer(500, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .subscribe { + // if app is backgrounded, display heads-up notification + val currentActivity = app.currentActivity + if (!app.isInForeground + || currentActivity !is HomeActivity + || !currentActivity.willNotifyAboutNewTx() + ) { + notificationHelper.postCustomLayoutTxNotification(inboundTxEventNotificationTxs.last()) + } + inboundTxEventNotificationTxs.clear() + } + } + + private fun sendPushNotificationToTxRecipient(recipientHex: String) { + walletInstance?.let { wallet -> + val senderHex = wallet.getWalletAddress().notificationHex() + notificationService.notifyRecipient(recipientHex, senderHex, wallet::signMessage) + } ?: logger.i("Wallet instance is null when trying to send push notification to recipient") + } + + private fun checkValidationResult(type: WalletValidationType, responseId: BigInteger, isSuccess: Boolean) { + try { + val currentStatus = walletValidationStatusMap[type] ?: return + if (currentStatus.requestKey != responseId) return + walletValidationStatusMap[type] = WalletValidationResult(currentStatus.requestKey, isSuccess) + logger.i("startSync:wallet validation:validation result: $type: $isSuccess") + checkBaseNodeSyncCompletion() + } catch (e: Throwable) { + logger.i(e.toString()) + } + } + + private fun checkBaseNodeSyncCompletion() { + // make a copy of the status map for concurrency protection + val statusMapCopy = walletValidationStatusMap.toMap() + // if base node not in sync, then switch to the next base node + // check if any has failed + val failed = statusMapCopy.any { it.value.isSuccess == false } + if (failed) { + walletValidationStatusMap.clear() + val currentBaseNode = baseNodesManager.currentBaseNode + if (currentBaseNode == null || !currentBaseNode.isCustom) { + baseNodesManager.setNextBaseNode() + syncBaseNode() + } + EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) // TODO replace with flow!!! + return + } + // if any of the results is null, we're still waiting for all callbacks to happen + val inProgress = statusMapCopy.any { it.value.isSuccess == null } + if (inProgress) { + return + } + // check if it's successful + val successful = statusMapCopy.all { it.value.isSuccess == true } + if (successful) { + walletValidationStatusMap.clear() + EventBus.baseNodeSyncState.post(BaseNodeSyncState.Online) // TODO replace with flow!!! + } + // shouldn't ever reach here - no-op + } + + enum class ConnectivityStatus(val value: Int) { + CONNECTING(0), + ONLINE(1), + OFFLINE(2), + } + + data class OutboundTxNotification(val txId: BigInteger, val recipientPublicKeyHex: String) + + enum class WalletValidationType { TXO, TX } + data class WalletValidationResult(val requestKey: BigInteger, val isSuccess: Boolean?) + + private fun runOnMain(block: suspend CoroutineScope.() -> Unit) { + applicationScope.launch(Dispatchers.Main) { block() } + } + + sealed class WalletEvent { + object Tx { + data class TxReceived(val tx: PendingInboundTx) : WalletEvent() + data class TxReplyReceived(val tx: PendingOutboundTx) : WalletEvent() + data class TxFinalized(val tx: PendingInboundTx) : WalletEvent() + data class InboundTxBroadcast(val tx: PendingInboundTx) : WalletEvent() + data class OutboundTxBroadcast(val tx: PendingOutboundTx) : WalletEvent() + data class TxMined(val tx: CompletedTx) : WalletEvent() + data class TxMinedUnconfirmed(val tx: CompletedTx, val confirmationCount: Int) : WalletEvent() + data class TxFauxConfirmed(val tx: CompletedTx) : WalletEvent() + data class TxFauxMinedUnconfirmed(val tx: CompletedTx, val confirmationCount: Int) : WalletEvent() + data class TxCancelled(val tx: CancelledTx) : WalletEvent() + data class DirectSendResult(val txId: TxId, val status: TransactionSendStatus) : WalletEvent() + } + } +} + +interface OutboundTxNotifier { + val outboundTxIdsToBePushNotified: CopyOnWriteArraySet } diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletStateHandler.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletStateHandler.kt index ab439e986..ae89b5ada 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletStateHandler.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletStateHandler.kt @@ -3,61 +3,46 @@ package com.tari.android.wallet.application.walletManager import com.orhanobut.logger.Logger import com.tari.android.wallet.ffi.FFIWallet import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.update import kotlinx.coroutines.withContext -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class WalletStateHandler @Inject constructor() { - private val logger - get() = Logger.t(this::class.simpleName) +private val logger + get() = Logger.t(WalletManager::class.simpleName) - private val _walletState = MutableStateFlow(WalletState.NotReady) - val walletState = _walletState.asStateFlow() - - fun setWalletState(state: WalletState) { - _walletState.update { state } - } - - suspend fun doOnWalletStarted(action: suspend (ffiWallet: FFIWallet) -> Unit) = withContext(Dispatchers.IO) { - walletState.firstOrNull { it is WalletState.Started } - ?.let { - action(FFIWallet.instance!!) - } ?: logger.i("Wallet service is not connected") - } +suspend fun WalletManager.doOnWalletStarted(action: suspend (ffiWallet: FFIWallet) -> Unit) = withContext(Dispatchers.IO) { + walletState.firstOrNull { it is WalletState.Started } + ?.let { + action(walletInstance!!) + } ?: logger.i("Wallet service is not connected") +} - suspend fun doOnWalletRunning(action: suspend (ffiWallet: FFIWallet) -> Unit) = withContext(Dispatchers.IO) { - walletState.firstOrNull { it is WalletState.Running } - ?.let { - action(FFIWallet.instance!!) - } ?: logger.i("Wallet service is not connected") - } +suspend fun WalletManager.doOnWalletRunning(action: suspend (ffiWallet: FFIWallet) -> Unit) = withContext(Dispatchers.IO) { + walletState.firstOrNull { it is WalletState.Running } + ?.let { + action(walletInstance!!) + } ?: logger.i("Wallet service is not connected") +} - suspend fun doOnWalletRunningWithValue(action: suspend (ffiWallet: FFIWallet) -> T): T = withContext(Dispatchers.IO) { - walletState.firstOrNull { it is WalletState.Running } - ?.let { - action(FFIWallet.instance!!) - } ?: error("Wallet service is not connected") - } +suspend fun WalletManager.doOnWalletRunningWithValue(action: suspend (ffiWallet: FFIWallet) -> T): T = withContext(Dispatchers.IO) { + walletState.firstOrNull { it is WalletState.Running } + ?.let { + action(walletInstance!!) + } ?: error("Wallet service is not connected") +} - suspend fun doOnWalletFailed(action: suspend (exception: Exception) -> Unit) = withContext(Dispatchers.IO) { - walletState - .debounce(300L) // todo this is a workaround for the issue that the wallet service is not connected yet - .firstOrNull { it is WalletState.Failed } - ?.let { - action((it as WalletState.Failed).exception) - } ?: logger.i("Wallet service is not connected") - } +suspend fun WalletManager.doOnWalletFailed(action: suspend (exception: Exception) -> Unit) = withContext(Dispatchers.IO) { + walletState + .debounce(300L) // todo this is a workaround for the issue that the wallet service is not connected yet + .firstOrNull { it is WalletState.Failed } + ?.let { + action((it as WalletState.Failed).exception) + } ?: logger.i("Wallet service is not connected") +} - suspend fun doOnWalletNotReady(action: suspend () -> Unit) = withContext(Dispatchers.IO) { - walletState.firstOrNull { it is WalletState.NotReady } - ?.let { - action() - } ?: logger.i("Wallet service is not connected") - } +suspend fun WalletManager.doOnWalletNotReady(action: suspend () -> Unit) = withContext(Dispatchers.IO) { + walletState.firstOrNull { it is WalletState.NotReady } + ?.let { + action() + } ?: logger.i("Wallet service is not connected") } diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt index a71d25b56..ccf0dc542 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt @@ -34,8 +34,6 @@ package com.tari.android.wallet.data.sharedPrefs.baseNode import android.content.SharedPreferences import com.tari.android.wallet.data.sharedPrefs.CommonPrefRepository -import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefBigIntegerDelegate -import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefBooleanNullableDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonNullableDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefIntDelegate @@ -43,7 +41,6 @@ import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.network.formatKey import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.service.baseNode.BaseNodeState -import java.math.BigInteger import javax.inject.Inject import javax.inject.Singleton @@ -57,7 +54,6 @@ class BaseNodePrefRepository @Inject constructor( const val CURRENT_BASE_NODE = "tari_wallet_current_base_node" const val USER_BASE_NODE_LIST = "tari_wallet_user_base_nodes" const val BASE_NODE_STATE = "tari_wallet_user_base_node_state" - const val BASE_NODE_LAST_SYNC_RESULT = "tari_wallet_base_node_last_sync_result" const val FFI_BASE_NODE_LIST = "FFI_BASE_NODE_LIST" } @@ -83,12 +79,6 @@ class BaseNodePrefRepository @Inject constructor( defValue = BaseNodeList(), ) - var baseNodeLastSyncResult: Boolean? by SharedPrefBooleanNullableDelegate( - prefs = sharedPrefs, - commonRepository = this, - name = Key.BASE_NODE_LAST_SYNC_RESULT, - ) - // ordinal value of BaseNodeState enum class private var baseNodeStateOrdinal: Int by SharedPrefIntDelegate( prefs = sharedPrefs, @@ -107,7 +97,6 @@ class BaseNodePrefRepository @Inject constructor( } fun clear() { - baseNodeLastSyncResult = null currentBaseNode = null userBaseNodes = BaseNodeList() ffiBaseNodes = BaseNodeList() diff --git a/app/src/main/java/com/tari/android/wallet/event/Event.kt b/app/src/main/java/com/tari/android/wallet/event/Event.kt index 663a0967c..99a9b0e82 100644 --- a/app/src/main/java/com/tari/android/wallet/event/Event.kt +++ b/app/src/main/java/com/tari/android/wallet/event/Event.kt @@ -32,11 +32,6 @@ */ package com.tari.android.wallet.event -import com.tari.android.wallet.model.CancelledTx -import com.tari.android.wallet.model.CompletedTx -import com.tari.android.wallet.model.PendingInboundTx -import com.tari.android.wallet.model.PendingOutboundTx -import com.tari.android.wallet.model.TransactionSendStatus import com.tari.android.wallet.model.TxId import com.tari.android.wallet.ui.fragment.send.finalize.TxFailureReason @@ -45,6 +40,8 @@ import com.tari.android.wallet.ui.fragment.send.finalize.TxFailureReason */ object Event { + // TODO use WalletManager.WalletEvent instead of these EventBus events + object App { class AppBackgrounded class AppForegrounded @@ -55,17 +52,7 @@ object Event { */ object Transaction { object Updated - data class TxReceived(val tx: PendingInboundTx) - data class TxReplyReceived(val tx: PendingOutboundTx) - data class TxFinalized(val tx: PendingInboundTx) - data class InboundTxBroadcast(val tx: PendingInboundTx) - data class OutboundTxBroadcast(val tx: PendingOutboundTx) - data class TxMined(val tx: CompletedTx) - data class TxMinedUnconfirmed(val tx: CompletedTx) - data class TxFauxConfirmed(val tx: CompletedTx) - data class TxFauxMinedUnconfirmed(val tx: CompletedTx) - data class TxCancelled(val tx: CancelledTx) - data class DirectSendResult(val txId: TxId, val status: TransactionSendStatus) + data class TxSendSuccessful(val txId: TxId) data class TxSendFailed(val failureReason: TxFailureReason) } diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt index 6ab45eb32..5807769f2 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt @@ -43,18 +43,14 @@ import com.tari.android.wallet.model.PendingInboundTx import com.tari.android.wallet.model.PendingOutboundTx import com.tari.android.wallet.model.PublicKey import com.tari.android.wallet.model.TariCoinPreview +import com.tari.android.wallet.model.TariContact import com.tari.android.wallet.model.TariUnblindedOutput import com.tari.android.wallet.model.TariVector import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.recovery.WalletRestorationResult import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository -import com.tari.android.wallet.util.Constants -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import java.math.BigInteger -import java.util.concurrent.atomic.AtomicReference /** * Wallet wrapper. @@ -62,42 +58,22 @@ import java.util.concurrent.atomic.AtomicReference * @author The Tari Development Team */ -@Suppress("MemberVisibilityCanBePrivate") class FFIWallet( private val sharedPrefsRepository: CorePrefRepository, private val securityPrefRepository: SecurityPrefRepository, private val seedPhraseRepository: SeedPhraseRepository, private val networkRepository: NetworkPrefRepository, private val commsConfig: FFICommsConfig, - private val logPath: String + private val logPath: String, + private val listener: FFIWalletListener ) : FFIBase() { - private val coroutineContext = Job() - private var localScope = CoroutineScope(coroutineContext) - - // values for the wallet initialization - private val logVerbosity: Int = if (BuildConfig.BUILD_TYPE == "debug") 11 else 4 - private val isDnsSecureOn = false - companion object { - private var atomicInstance = AtomicReference() - var instance: FFIWallet? - get() = atomicInstance.get() - set(value) = atomicInstance.set(value) - - fun getOrNull(block: (FFIWallet) -> T): T? { - return instance.takeIf { it != null }?.let { wallet -> - try { - block(wallet) - } catch (e: Exception) { - logger.e("FFIWallet block failed with exception: $e") - null - } - } ?: run { - logger.i("Trying to access FFIWallet instance before it is initialized.") - null - } - } + // values for the wallet initialization + private val LOG_VERBOSITY: Int = if (BuildConfig.BUILD_TYPE == "debug") 11 else 4 + private const val IS_DNS_SECURE_ON = false + private const val MAX_NUMBER_OF_ROLLING_LOG_FILES = 2 + private const val ROLLING_LOG_FILE_MAX_SIZE_BYTES = 10 * 1024 * 1024 } private external fun jniCreate( @@ -149,35 +125,20 @@ class FFIWallet( ) private external fun jniGetBalance(libError: FFIError): FFIPointer - private external fun jniLogMessage(message: String, libError: FFIError) - private external fun jniGetWalletAddress(libError: FFIError): FFIPointer - private external fun jniGetContacts(libError: FFIError): FFIPointer - private external fun jniAddUpdateContact(contactPtr: FFIContact, libError: FFIError): Boolean - private external fun jniRemoveContact(contactPtr: FFIContact, libError: FFIError): Boolean - private external fun jniGetCompletedTxs(libError: FFIError): FFIPointer - private external fun jniGetCancelledTxs(libError: FFIError): FFIPointer - private external fun jniGetCompletedTxById(id: String, libError: FFIError): FFIPointer - private external fun jniGetCancelledTxById(id: String, libError: FFIError): FFIPointer - private external fun jniGetPendingOutboundTxs(libError: FFIError): FFIPointer - private external fun jniGetPendingOutboundTxById(id: String, libError: FFIError): FFIPointer - private external fun jniGetPendingInboundTxs(libError: FFIError): FFIPointer - private external fun jniGetPendingInboundTxById(id: String, libError: FFIError): FFIPointer - private external fun jniCancelPendingTx(id: String, libError: FFIError): Boolean - private external fun jniSendTx( publicKeyPtr: FFITariWalletAddress, amount: String, @@ -189,61 +150,37 @@ class FFIWallet( ): ByteArray private external fun jniSignMessage(message: String, libError: FFIError): String - private external fun jniVerifyMessageSignature(publicKeyPtr: FFIPublicKey, message: String, signature: String, libError: FFIError): Boolean - private external fun jniGetBaseNodePeers(libError: FFIError): FFIPointer - private external fun jniAddBaseNodePeer(publicKey: FFIPublicKey, address: String, libError: FFIError): Boolean - private external fun jniStartTXOValidation(libError: FFIError): ByteArray - private external fun jniStartTxValidation(libError: FFIError): ByteArray - private external fun jniRestartTxBroadcast(libError: FFIError): ByteArray - private external fun jniPowerModeNormal(libError: FFIError) - private external fun jniPowerModeLow(libError: FFIError) - private external fun jniGetSeedWords(libError: FFIError): FFIPointer - private external fun jniSetKeyValue(key: String, value: String, libError: FFIError): Boolean - private external fun jniGetKeyValue(key: String, libError: FFIError): String - private external fun jniRemoveKeyValue(key: String, libError: FFIError): Boolean - private external fun jniGetConfirmations(libError: FFIError): ByteArray - private external fun jniSetConfirmations(number: String, libError: FFIError) - private external fun jniEstimateTxFee(amount: String, gramFee: String, kernelCount: String, outputCount: String, libError: FFIError): ByteArray - private external fun jniStartRecovery( - base_node_public_key: FFIPublicKey, + baseNodePublicKey: FFIPublicKey, callback: String, - callback_sig: String, + callbackSig: String, recoveryOutputMessage: String, libError: FFIError ): Boolean private external fun jniWalletGetFeePerGramStats(count: Int, libError: FFIError): FFIPointer - private external fun jniGetUtxos(page: Int, pageSize: Int, sorting: Int, dustThreshold: Long, libError: FFIError): FFIPointer - private external fun jniGetAllUtxos(libError: FFIError): FFIPointer - private external fun jniJoinUtxos(commitments: Array, feePerGram: String, libError: FFIError): FFIPointer - private external fun jniSplitUtxos(commitments: Array, splitCount: String, feePerGram: String, libError: FFIError): FFIPointer - private external fun jniPreviewJoinUtxos(commitments: Array, feePerGram: String, libError: FFIError): FFIPointer - private external fun jniPreviewSplitUtxos(commitments: Array, splitCount: String, feePerGram: String, libError: FFIError): FFIPointer - private external fun jniWalletGetUnspentOutputs(libError: FFIError): FFIPointer - private external fun jniImportExternalUtxoAsNonRewindable( output: FFITariUnblindedOutput, sourceAddress: FFITariWalletAddress, @@ -253,9 +190,6 @@ class FFIWallet( private external fun jniDestroy() - - var listener: FFIWalletListener? = null - // this acts as a constructor would for a normal class since constructors are not allowed for // singletons init { @@ -278,14 +212,14 @@ class FFIWallet( jniCreate( commsConfig = commsConfig, logPath = logPath, - logVerbosity = logVerbosity, - maxNumberOfRollingLogFiles = Constants.Wallet.MAX_NUMBER_OF_ROLLING_LOG_FILES, - rollingLogFileMaxSizeBytes = Constants.Wallet.ROLLING_LOG_FILE_MAX_SIZE_BYTES, + logVerbosity = LOG_VERBOSITY, + maxNumberOfRollingLogFiles = MAX_NUMBER_OF_ROLLING_LOG_FILES, + rollingLogFileMaxSizeBytes = ROLLING_LOG_FILE_MAX_SIZE_BYTES, passphrase = passphrase, network = networkRepository.currentNetwork.network.uriComponent, seedWords = seedPhraseRepository.getPhrase()?.ffiSeedWords, dnsPeer = networkRepository.currentNetwork.dnsPeer, - isDnsSecureOn = isDnsSecureOn, + isDnsSecureOn = IS_DNS_SECURE_ON, this::onTxReceived.name, "(J)V", this::onTxReplyReceived.name, "(J)V", this::onTxFinalized.name, "(J)V", @@ -351,133 +285,6 @@ class FFIWallet( fun cancelPendingTx(id: BigInteger): Boolean = runWithError { jniCancelPendingTx(id.toString(), it) } - fun onTxReceived(pendingInboundTxPtr: FFIPointer) { - val tx = FFIPendingInboundTx(pendingInboundTxPtr) - logger.i("Tx received ${tx.getId()}") - val pendingTx = PendingInboundTx(tx) - localScope.launch { listener?.onTxReceived(pendingTx) } - } - - /** - * This callback function cannot be private due to JNI behaviour - */ - fun onTxReplyReceived(txPointer: FFIPointer) { - val tx = FFICompletedTx(txPointer) - logger.i("Tx reply received ${tx.getId()}") - val pendingOutboundTx = PendingOutboundTx(tx) - localScope.launch { listener?.onTxReplyReceived(pendingOutboundTx) } - } - - fun onTxFinalized(completedTx: FFIPointer) { - val tx = FFICompletedTx(completedTx) - logger.i("Tx finalized ${tx.getId()}") - val pendingInboundTx = PendingInboundTx(tx) - localScope.launch { listener?.onTxFinalized(pendingInboundTx) } - } - - fun onTxBroadcast(completedTxPtr: FFIPointer) { - val tx = FFICompletedTx(completedTxPtr) - logger.i("Tx broadcast ${tx.getId()}") - when (tx.getDirection()) { - Tx.Direction.INBOUND -> { - val pendingInboundTx = PendingInboundTx(tx) - localScope.launch { listener?.onInboundTxBroadcast(pendingInboundTx) } - } - - Tx.Direction.OUTBOUND -> { - val pendingOutboundTx = PendingOutboundTx(tx) - localScope.launch { listener?.onOutboundTxBroadcast(pendingOutboundTx) } - } - } - } - - fun onTxMined(completedTxPtr: FFIPointer) { - val completed = CompletedTx(completedTxPtr) - logger.i("Tx mined & confirmed ${completed.id}") - localScope.launch { listener?.onTxMined(completed) } - } - - fun onTxMinedUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { - val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() - val completed = CompletedTx(completedTxPtr) - logger.i("Tx mined & unconfirmed ${completed.id} $confirmationCount") - localScope.launch { listener?.onTxMinedUnconfirmed(completed, confirmationCount) } - } - - fun onTxFauxConfirmed(completedTxPtr: FFIPointer) { - val completed = CompletedTx(completedTxPtr) - logger.i("Tx faux confirmed ${completed.id}") - localScope.launch { listener?.onTxMined(completed) } - } - - fun onBaseNodeStatus(baseNodeStatePointer: FFIPointer) { - val baseNodeState = FFITariBaseNodeState(baseNodeStatePointer) - logger.i("Base node state updated (height of the longest chain is ${baseNodeState.getHeightOfLongestChain()})") - localScope.launch { listener?.onBaseNodeStateChanged(baseNodeState) } - } - - fun onTxFauxUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { - val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() - val completed = CompletedTx(completedTxPtr) - logger.i("Tx faux unconfirmed ${completed.id}") - localScope.launch { listener?.onTxMinedUnconfirmed(completed, confirmationCount) } - } - - fun onDirectSendResult(bytes: ByteArray, pointer: FFIPointer) { - val txId = BigInteger(1, bytes) - logger.i("Tx direct send result $txId") - localScope.launch { listener?.onDirectSendResult(txId, FFITransactionSendStatus(pointer).getStatus()) } - } - - fun onTxCancelled(completedTx: FFIPointer, rejectionReason: ByteArray) { - val rejectionReasonInt = BigInteger(1, rejectionReason).toInt() - val tx = FFICompletedTx(completedTx) - logger.i("Tx cancelled ${tx.getId()}") - - if (tx.getDirection() == Tx.Direction.OUTBOUND) { - localScope.launch { listener?.onTxCancelled(CancelledTx(tx), rejectionReasonInt) } - } - } - - fun onConnectivityStatus(bytes: ByteArray) { - val connectivityStatus = BigInteger(1, bytes) - localScope.launch { listener?.onConnectivityStatus(connectivityStatus.toInt()) } - logger.i("ConnectivityStatus is [$connectivityStatus]") - } - - fun onWalletScannedHeight(bytes: ByteArray) { - val height = BigInteger(1, bytes) - localScope.launch { listener?.onWalletScannedHeight(height.toInt()) } - logger.i("Wallet scanned height is [$height]") - } - - fun onBalanceUpdated(ptr: FFIPointer) { - logger.i("Balance Updated") - val balance = FFIBalance(ptr).runWithDestroy { BalanceInfo(it.getAvailable(), it.getIncoming(), it.getOutgoing(), it.getTimeLocked()) } - localScope.launch { listener?.onBalanceUpdated(balance) } - } - - fun onTXOValidationComplete(bytes: ByteArray, statusBytes: ByteArray) { - val requestId = BigInteger(1, bytes) - val statusInteger = BigInteger(1, statusBytes).toInt() - val status = TransactionValidationStatus.entries.firstOrNull { it.value == statusInteger } ?: return - logger.i("TXO validation [$requestId] complete. Result: $status") - localScope.launch { listener?.onTXOValidationComplete(requestId, status) } - } - - fun onTxValidationComplete(requestIdBytes: ByteArray, statusBytes: ByteArray) { - val requestId = BigInteger(1, requestIdBytes) - val statusInteger = BigInteger(1, statusBytes).toInt() - val status = TransactionValidationStatus.entries.firstOrNull { it.value == statusInteger } ?: return - logger.i("Tx validation [$requestId] complete. Result: $status") - localScope.launch { listener?.onTxValidationComplete(requestId, status) } - } - - @Suppress("MemberVisibilityCanBePrivate", "UNUSED_PARAMETER") - fun onContactLivenessDataUpdated(livenessUpdate: FFIPointer) { - logger.i("OnContactLivenessDataUpdated") - } - fun estimateTxFee(amount: BigInteger, gramFee: BigInteger, kernelCount: BigInteger, outputCount: BigInteger): BigInteger = runWithError { BigInteger(1, jniEstimateTxFee(amount.toString(), gramFee.toString(), kernelCount.toString(), outputCount.toString(), it)) } @@ -573,15 +380,156 @@ class FFIWallet( } } - fun onWalletRecovery(event: Int, firstArg: ByteArray, secondArg: ByteArray) { + private fun onWalletRecovery(event: Int, firstArg: ByteArray, secondArg: ByteArray) { val result = WalletRestorationResult.create(event, firstArg, secondArg) logger.i("Wallet restored with $result") - localScope.launch { listener?.onWalletRestoration(result) } + listener.onWalletRestoration(result) } override fun destroy() { - listener = null jniDestroy() } -} + /* FFI wallet callbacks */ + + private fun onTxReceived(pendingInboundTxPtr: FFIPointer) { + val tx = FFIPendingInboundTx(pendingInboundTxPtr) + logger.i("Tx received ${tx.getId()}") + val pendingTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + listener.onTxReceived(pendingTx) + } + + private fun onTxReplyReceived(txPointer: FFIPointer) { + val tx = FFICompletedTx(txPointer) + logger.i("Tx reply received ${tx.getId()}") + val pendingOutboundTx = PendingOutboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + listener.onTxReplyReceived(pendingOutboundTx) + } + + private fun onTxFinalized(completedTx: FFIPointer) { + val tx = FFICompletedTx(completedTx) + logger.i("Tx finalized ${tx.getId()}") + val pendingInboundTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + listener.onTxFinalized(pendingInboundTx) + } + + private fun onTxBroadcast(completedTxPtr: FFIPointer) { + val tx = FFICompletedTx(completedTxPtr) + logger.i("Tx broadcast ${tx.getId()}") + when (tx.getDirection()) { + Tx.Direction.INBOUND -> { + val pendingInboundTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + listener.onInboundTxBroadcast(pendingInboundTx) + } + + Tx.Direction.OUTBOUND -> { + val pendingOutboundTx = PendingOutboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + listener.onOutboundTxBroadcast(pendingOutboundTx) + } + } + } + + private fun onTxMined(completedTxPtr: FFIPointer) { + val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + logger.i("Tx mined & confirmed ${completed.id}") + listener.onTxMined(completed) + } + + private fun onTxMinedUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { + val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() + val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + logger.i("Tx mined & unconfirmed ${completed.id} $confirmationCount") + listener.onTxMinedUnconfirmed(completed, confirmationCount) + } + + private fun onTxFauxConfirmed(completedTxPtr: FFIPointer) { + val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + logger.i("Tx faux confirmed ${completed.id}") + listener.onTxMined(completed) + } + + private fun onTxFauxUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { + val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() + val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + logger.i("Tx faux unconfirmed ${completed.id}") + listener.onTxMinedUnconfirmed(completed, confirmationCount) + } + + private fun onDirectSendResult(bytes: ByteArray, pointer: FFIPointer) { + val txId = BigInteger(1, bytes) + logger.i("Tx direct send result $txId") + listener.onDirectSendResult(txId, FFITransactionSendStatus(pointer).getStatus()) + } + + private fun onTxCancelled(completedTx: FFIPointer, rejectionReason: ByteArray) { + val rejectionReasonInt = BigInteger(1, rejectionReason).toInt() + val tx = FFICompletedTx(completedTx) + logger.i("Tx cancelled ${tx.getId()}") + val cancelledTx = CancelledTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + if (tx.getDirection() == Tx.Direction.OUTBOUND) { + listener.onTxCancelled(cancelledTx, rejectionReasonInt) + } + } + + private fun onBaseNodeStatus(baseNodeStatePointer: FFIPointer) { + val baseNodeState = FFITariBaseNodeState(baseNodeStatePointer) + logger.i("Base node state updated (height of the longest chain is ${baseNodeState.getHeightOfLongestChain()})") + listener.onBaseNodeStateChanged(baseNodeState) + } + + private fun onConnectivityStatus(bytes: ByteArray) { + val connectivityStatus = BigInteger(1, bytes) + logger.i("ConnectivityStatus is [$connectivityStatus]") + listener.onConnectivityStatus(connectivityStatus.toInt()) + } + + private fun onWalletScannedHeight(bytes: ByteArray) { + val height = BigInteger(1, bytes) + logger.i("Wallet scanned height is [$height]") + listener.onWalletScannedHeight(height.toInt()) + } + + private fun onBalanceUpdated(ptr: FFIPointer) { + logger.i("Balance Updated") + val balance = FFIBalance(ptr).runWithDestroy { BalanceInfo(it.getAvailable(), it.getIncoming(), it.getOutgoing(), it.getTimeLocked()) } + listener.onBalanceUpdated(balance) + } + + private fun onTXOValidationComplete(bytes: ByteArray, statusBytes: ByteArray) { + val requestId = BigInteger(1, bytes) + val statusInteger = BigInteger(1, statusBytes).toInt() + val status = TransactionValidationStatus.entries.firstOrNull { it.value == statusInteger } ?: return + logger.i("TXO validation [$requestId] complete. Result: $status") + listener.onTXOValidationComplete(requestId, status) + } + + private fun onTxValidationComplete(requestIdBytes: ByteArray, statusBytes: ByteArray) { + val requestId = BigInteger(1, requestIdBytes) + val statusInteger = BigInteger(1, statusBytes).toInt() + val status = TransactionValidationStatus.entries.firstOrNull { it.value == statusInteger } ?: return + logger.i("Tx validation [$requestId] complete. Result: $status") + listener.onTxValidationComplete(requestId, status) + } + + private fun onContactLivenessDataUpdated(livenessUpdate: FFIPointer) { + logger.i("OnContactLivenessDataUpdated") + } + + private fun getUserByWalletAddress(address: TariWalletAddress): TariContact { + val contactsFFI = getContacts() + for (i in 0 until contactsFFI.getLength()) { + val contactFFI = contactsFFI.getAt(i) + val walletAddressFFI = contactFFI.getWalletAddress() + val tariContact = if (TariWalletAddress(walletAddressFFI) == address) TariContact(contactFFI) else null + walletAddressFFI.destroy() + contactFFI.destroy() + if (tariContact != null) { + contactsFFI.destroy() + return tariContact + } + } + // destroy native collection + contactsFFI.destroy() + return TariContact(address) + } +} diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt index 3de821383..516da8913 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt @@ -56,7 +56,7 @@ interface FFIWalletListener { fun onTxValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) fun onBalanceUpdated(balanceInfo: BalanceInfo) fun onConnectivityStatus(status: Int) - fun onWalletScannedHeight(height: Int) fun onWalletRestoration(result: WalletRestorationResult) + fun onWalletScannedHeight(height: Int) fun onBaseNodeStateChanged(baseNodeState: FFITariBaseNodeState) } diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupFileProcessor.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupFileProcessor.kt index 8006149d6..5a0303fe1 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupFileProcessor.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupFileProcessor.kt @@ -34,12 +34,12 @@ package com.tari.android.wallet.infrastructure.backup import com.google.gson.Gson import com.orhanobut.logger.Logger +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.backup.BackupPrefRepository import com.tari.android.wallet.data.sharedPrefs.security.SecurityPrefRepository import com.tari.android.wallet.extension.encrypt import com.tari.android.wallet.ffi.FFIError -import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.infrastructure.backup.compress.CompressionMethod import com.tari.android.wallet.infrastructure.security.encryption.SymmetricEncryptionAlgorithm import com.tari.android.wallet.model.fullBase58 @@ -57,6 +57,7 @@ class BackupFileProcessor @Inject constructor( private val securityPrefRepository: SecurityPrefRepository, private val walletConfig: WalletConfig, private val namingPolicy: BackupNamingPolicy, + private val walletManager: WalletManager, ) { private val logger get() = Logger.t(BackupFileProcessor::class.simpleName) @@ -77,7 +78,7 @@ class BackupFileProcessor @Inject constructor( if (backupPassword.isNullOrEmpty()) { val mimeType = "application/json" - val ffiWallet = FFIWallet.instance!! + val ffiWallet = walletManager.walletInstance ?: error("Wallet is not initialized during backup") val json = Gson().toJson( BackupUtxos( @@ -157,4 +158,3 @@ class BackupFileProcessor @Inject constructor( } } } - diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt index b5e19a12b..0f0d0364f 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt @@ -37,7 +37,11 @@ import android.content.Intent import androidx.fragment.app.Fragment import com.orhanobut.logger.Logger import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent +import com.tari.android.wallet.data.sharedPrefs.backup.BackupPrefRepository import com.tari.android.wallet.data.sharedPrefs.delegates.SerializableTime +import com.tari.android.wallet.di.ApplicationScope import com.tari.android.wallet.event.Event import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.infrastructure.backup.dropbox.DropboxBackupStorage @@ -46,10 +50,8 @@ import com.tari.android.wallet.infrastructure.backup.local.LocalBackupStorage import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions -import com.tari.android.wallet.data.sharedPrefs.backup.BackupPrefRepository import io.reactivex.subjects.BehaviorSubject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -66,7 +68,9 @@ class BackupManager @Inject constructor( private val localFileBackupStorage: LocalBackupStorage, private val googleDriveBackupStorage: GoogleDriveBackupStorage, private val dropboxBackupStorage: DropboxBackupStorage, - private val notificationHelper: NotificationHelper + private val notificationHelper: NotificationHelper, + private val walletManager: WalletManager, + @ApplicationScope private val applicationScope: CoroutineScope, ) { private val logger @@ -74,14 +78,11 @@ class BackupManager @Inject constructor( var currentOption: BackupOptions? = BackupOptions.Dropbox - private val coroutineContext = Job() - private var localScope = CoroutineScope(coroutineContext) - private val backupMutex = Mutex() private val trigger = BehaviorSubject.create() - private val debouncedJob = trigger.debounce(300L, TimeUnit.MILLISECONDS) - .doOnEach { localScope.launch { backupAll() } } + private val debouncedJob = trigger.debounce(300L, TimeUnit.MILLISECONDS) // TODO don't use rx for debounce + .doOnEach { applicationScope.launch { backupAll() } } .subscribe() init { @@ -90,6 +91,24 @@ class BackupManager @Inject constructor( EventBus.subscribe(this) { trigger.onNext(Unit) } EventBus.subscribe(this) { trigger.onNext(Unit) } + + applicationScope.launch { + walletManager.walletEvent.collect { event -> + when (event) { + is WalletEvent.Tx.TxReceived, + is WalletEvent.Tx.TxReplyReceived, + is WalletEvent.Tx.TxFinalized, + is WalletEvent.Tx.InboundTxBroadcast, + is WalletEvent.Tx.OutboundTxBroadcast, + is WalletEvent.Tx.TxMined, + is WalletEvent.Tx.TxMinedUnconfirmed, + is WalletEvent.Tx.TxFauxConfirmed, + is WalletEvent.Tx.TxFauxMinedUnconfirmed, + is WalletEvent.Tx.DirectSendResult, + is WalletEvent.Tx.TxCancelled -> trigger.onNext(Unit) + } + } + } } fun setupStorage(option: BackupOptions, hostFragment: Fragment) { @@ -148,7 +167,7 @@ class BackupManager @Inject constructor( } } - fun turnOffAll() = localScope.launch { + fun turnOffAll() = applicationScope.launch { backupSettingsRepository.getOptionList.forEach { turnOff(it.type) } } @@ -160,7 +179,7 @@ class BackupManager @Inject constructor( backupsState.copy(backupsStates = backupsState.backupsStates.toMutableMap().also { it[optionType] = BackupState.BackupDisabled }) EventBus.backupState.post(newState) val backupStorage = getStorageByOption(optionType) - localScope.launch { backupStorage.signOut() } + applicationScope.launch { backupStorage.signOut() } } suspend fun signOut() { diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupUtxos.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupUtxos.kt index 7b440f64b..091440c1f 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupUtxos.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupUtxos.kt @@ -2,4 +2,4 @@ package com.tari.android.wallet.infrastructure.backup import com.tari.android.wallet.ffi.Base58 -data class BackupUtxos(val utxos: List?, val sourceBase58: Base58) \ No newline at end of file +data class BackupUtxos(val utxos: List, val sourceBase58: Base58) \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/FFIFileAdapter.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/FFIFileAdapter.kt index 0edf6ec83..0dc024aaf 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/FFIFileAdapter.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/FFIFileAdapter.kt @@ -7,7 +7,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter -class FFIFileAdapter : LogAdapter { +class FFIFileAdapter(private val wallet: FFIWallet?) : LogAdapter { override fun isLoggable(priority: Int, tag: String?): Boolean = true @@ -25,7 +25,7 @@ class FFIFileAdapter : LogAdapter { } val dateTimeNow = dateTimeFormatter.format(LocalDateTime.now()) val debugLine = "$dateTimeNow [${tag ?: ""}] $priorityName ${message.replace("\n", " ")}" - FFIWallet.instance?.logMessage(debugLine) + wallet?.logMessage(debugLine) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/LoggerAdapter.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/LoggerAdapter.kt index debadf79c..84f1bf293 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/LoggerAdapter.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/LoggerAdapter.kt @@ -5,6 +5,7 @@ import com.orhanobut.logger.FormatStrategy import com.orhanobut.logger.Logger import com.orhanobut.logger.PrettyFormatStrategy import com.tari.android.wallet.BuildConfig +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.sentry.SentryPrefRepository import javax.inject.Inject @@ -12,7 +13,11 @@ import javax.inject.Singleton @Singleton -class LoggerAdapter @Inject constructor(val walletConfig: WalletConfig, private val sentryPrefRepository: SentryPrefRepository) { +class LoggerAdapter @Inject constructor( + private val walletConfig: WalletConfig, + private val walletManager: WalletManager, + private val sentryPrefRepository: SentryPrefRepository, +) { fun init() { val formatStrategy: FormatStrategy = PrettyFormatStrategy.newBuilder() .showThreadInfo(false) // (Optional) Whether to show thread info or not. Default true @@ -22,7 +27,7 @@ class LoggerAdapter @Inject constructor(val walletConfig: WalletConfig, private .build() Logger.addLogAdapter(AndroidLogAdapter(formatStrategy)) - Logger.addLogAdapter(FFIFileAdapter()) + Logger.addLogAdapter(FFIFileAdapter(walletManager.walletInstance)) @Suppress("KotlinConstantConditions") if (BuildConfig.FLAVOR != "privacy") { Logger.addLogAdapter(SentryLogAdapter(walletConfig, sentryPrefRepository)) diff --git a/app/src/main/java/com/tari/android/wallet/model/TariContact.kt b/app/src/main/java/com/tari/android/wallet/model/TariContact.kt index 18ab06cac..f9e9bacc7 100644 --- a/app/src/main/java/com/tari/android/wallet/model/TariContact.kt +++ b/app/src/main/java/com/tari/android/wallet/model/TariContact.kt @@ -48,15 +48,11 @@ data class TariContact( val isFavorite: Boolean = false, ) : Parcelable { - // TODO create a constructor that takes an wallet address only and use normalized alias - constructor(ffiContact: FFIContact) : this( walletAddress = TariWalletAddress(ffiContact.getWalletAddress()), alias = ffiContact.getAlias(), isFavorite = ffiContact.getIsFavorite() ) - fun filtered(text: String): Boolean = walletAddress.fullEmojiId.contains(text, ignoreCase = true) || alias.contains(text, ignoreCase = true) - override fun toString() = "Contact(alias='$alias') ${super.toString()}" } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt b/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt index c375459b6..ac150ace0 100644 --- a/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt +++ b/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt @@ -19,6 +19,7 @@ class SeedPhrase { this.ffiSeedWords = ffiSeedWords SeedPhraseCreationResult.Success } + SeedWordsWordPushResult.InvalidSeedPhrase -> SeedPhraseCreationResult.InvalidSeedPhrase } } @@ -31,14 +32,14 @@ class SeedPhrase { sealed class SeedPhraseCreationResult { - object Success : SeedPhraseCreationResult() - class Failed(val exception: Throwable) : SeedPhraseCreationResult() - object InvalidSeedPhrase : SeedPhraseCreationResult() - object SeedPhraseNotCompleted : SeedPhraseCreationResult() - object InvalidSeedWord : SeedPhraseCreationResult() + data object Success : SeedPhraseCreationResult() + data class Failed(val exception: Throwable) : SeedPhraseCreationResult() + data object InvalidSeedPhrase : SeedPhraseCreationResult() + data object SeedPhraseNotCompleted : SeedPhraseCreationResult() + data object InvalidSeedWord : SeedPhraseCreationResult() } companion object { - const val SeedPhraseLength: Int = 24 + const val SEED_PHRASE_LENGTH: Int = 24 } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/service/BaseNodeValidationType.kt b/app/src/main/java/com/tari/android/wallet/service/service/BaseNodeValidationType.kt deleted file mode 100644 index e8fba37e9..000000000 --- a/app/src/main/java/com/tari/android/wallet/service/service/BaseNodeValidationType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tari.android.wallet.service.service - -enum class BaseNodeValidationType { - TXO, - TX; -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/service/FFIWalletListenerImpl.kt b/app/src/main/java/com/tari/android/wallet/service/service/FFIWalletListenerImpl.kt deleted file mode 100644 index bf857471d..000000000 --- a/app/src/main/java/com/tari/android/wallet/service/service/FFIWalletListenerImpl.kt +++ /dev/null @@ -1,343 +0,0 @@ -package com.tari.android.wallet.service.service - -import com.orhanobut.logger.Logger -import com.tari.android.wallet.application.TariWalletApplication -import com.tari.android.wallet.application.baseNodes.BaseNodesManager -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository -import com.tari.android.wallet.event.Event -import com.tari.android.wallet.event.EventBus -import com.tari.android.wallet.ffi.FFITariBaseNodeState -import com.tari.android.wallet.ffi.FFIWallet -import com.tari.android.wallet.ffi.FFIWalletListener -import com.tari.android.wallet.ffi.TransactionValidationStatus -import com.tari.android.wallet.infrastructure.backup.BackupManager -import com.tari.android.wallet.model.BalanceInfo -import com.tari.android.wallet.model.CancelledTx -import com.tari.android.wallet.model.CompletedTx -import com.tari.android.wallet.model.PendingInboundTx -import com.tari.android.wallet.model.PendingOutboundTx -import com.tari.android.wallet.model.TariContact -import com.tari.android.wallet.model.TariWalletAddress -import com.tari.android.wallet.model.TransactionSendStatus -import com.tari.android.wallet.model.Tx -import com.tari.android.wallet.model.TxId -import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.notification.NotificationHelper -import com.tari.android.wallet.service.TariWalletServiceListener -import com.tari.android.wallet.service.baseNode.BaseNodeState -import com.tari.android.wallet.service.baseNode.BaseNodeSyncState -import com.tari.android.wallet.service.notification.NotificationService -import com.tari.android.wallet.ui.fragment.home.HomeActivity -import io.reactivex.Observable -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import java.math.BigInteger -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentMap -import java.util.concurrent.CopyOnWriteArrayList -import java.util.concurrent.CopyOnWriteArraySet -import java.util.concurrent.TimeUnit - -class FFIWalletListenerImpl( - private val wallet: FFIWallet, - private val backupManager: BackupManager, - private val notificationHelper: NotificationHelper, - private val notificationService: NotificationService, - private val app: TariWalletApplication, - private val baseNodeSharedPrefsRepository: BaseNodePrefRepository, - private val baseNodesManager: BaseNodesManager -) : FFIWalletListener { - - private val logger - get() = Logger.t("FFIWalletListenerImpl") - var listeners = CopyOnWriteArrayList() - - /** - * Maps the validation type to the request id and validation result. This map will be - * initialized at the beginning of each base node validation sequence. - * Validation results will all be null, and will be set as the result callbacks get called. - */ - var baseNodeValidationStatusMap: ConcurrentMap> = ConcurrentHashMap() - - /** - * Debounce for inbound transaction notification. - */ - private var txReceivedNotificationDelayedAction: Disposable? = null - private var inboundTxEventNotificationTxs = mutableListOf() - - private var txBroadcastRestarted = false - - val outboundTxIdsToBePushNotified = CopyOnWriteArraySet() - - override fun onTxReceived(pendingInboundTx: PendingInboundTx) { - val newTx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxReceived(newTx)) - // manage notifications - postTxNotification(newTx) - listeners.forEach { it.onTxReceived(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxReplyReceived(pendingOutboundTx: PendingOutboundTx) { - val newTx = pendingOutboundTx.copy(tariContact = getUserByWalletAddress(pendingOutboundTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxReplyReceived(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxReplyReceived(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxFinalized(pendingInboundTx: PendingInboundTx) { - val newTx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxFinalized(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxFinalized(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onInboundTxBroadcast(pendingInboundTx: PendingInboundTx) { - val newTx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.InboundTxBroadcast(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onInboundTxBroadcast(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onOutboundTxBroadcast(pendingOutboundTx: PendingOutboundTx) { - val newTx = pendingOutboundTx.copy(tariContact = getUserByWalletAddress(pendingOutboundTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.OutboundTxBroadcast(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onOutboundTxBroadcast(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxMined(completedTx: CompletedTx) { - val newTx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxMined(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxMined(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxMinedUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) { - val newTx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxMinedUnconfirmed(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxMinedUnconfirmed(newTx, confirmationCount) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxFauxConfirmed(completedTx: CompletedTx) { - val newTx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxFauxConfirmed(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxFauxConfirmed(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTxFauxUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) { - val newTx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)) - // post event to bus for the listeners - EventBus.post(Event.Transaction.TxFauxMinedUnconfirmed(newTx)) - // notify external listeners - listeners.iterator().forEach { it.onTxFauxUnconfirmed(newTx, confirmationCount) } - // schedule a backup - backupManager.backupNow() - } - - override fun onDirectSendResult(txId: BigInteger, status: TransactionSendStatus) { - // post event to bus - EventBus.post(Event.Transaction.DirectSendResult(TxId(txId), status)) - outboundTxIdsToBePushNotified.firstOrNull { it.txId == txId }?.let { - outboundTxIdsToBePushNotified.remove(it) - sendPushNotificationToTxRecipient(it.recipientPublicKeyHex) - } - // schedule a backup - backupManager.backupNow() - // notify external listeners - listeners.iterator().forEach { it.onDirectSendResult(TxId(txId), status) } - } - - override fun onTxCancelled(cancelledTx: CancelledTx, rejectionReason: Int) { - val newTx = cancelledTx.copy(tariContact = getUserByWalletAddress(cancelledTx.tariContact.walletAddress)) - // post event to bus - EventBus.post(Event.Transaction.TxCancelled(newTx)) - val currentActivity = app.currentActivity - if (cancelledTx.direction == Tx.Direction.INBOUND && !(app.isInForeground && currentActivity is HomeActivity && currentActivity.willNotifyAboutNewTx()) - ) { - notificationHelper.postTxCanceledNotification(newTx) - } - // notify external listeners - listeners.iterator().forEach { listener -> listener.onTxCancelled(newTx) } - // schedule a backup - backupManager.backupNow() - } - - override fun onTXOValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) { - checkValidationResult(BaseNodeValidationType.TXO, responseId, status == TransactionValidationStatus.Success) - } - - override fun onTxValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) { - checkValidationResult(BaseNodeValidationType.TX, responseId, status == TransactionValidationStatus.Success) - if (!txBroadcastRestarted && status == TransactionValidationStatus.Success) { - wallet.restartTxBroadcast() - txBroadcastRestarted = true - } - } - - override fun onBalanceUpdated(balanceInfo: BalanceInfo) { - EventBus.balanceState.post(balanceInfo) - // notify external listeners - listeners.iterator().forEach { it.onBalanceUpdated(balanceInfo) } - } - - override fun onConnectivityStatus(status: Int) { - when (ConnectivityStatus.entries[status]) { - ConnectivityStatus.CONNECTING -> { - /* do nothing */ - } - - ConnectivityStatus.ONLINE -> { - baseNodesManager.refreshBaseNodeList() - baseNodeSharedPrefsRepository.baseNodeState = BaseNodeState.Online - EventBus.baseNodeState.post(BaseNodeState.Online) - listeners.iterator().forEach { it.onBaseNodeSyncComplete(true) } - } - - ConnectivityStatus.OFFLINE -> { - val currentBaseNode = baseNodeSharedPrefsRepository.currentBaseNode - if (currentBaseNode == null || !currentBaseNode.isCustom) { - baseNodesManager.setNextBaseNode() - baseNodesManager.startSync() - } - baseNodeSharedPrefsRepository.baseNodeState = BaseNodeState.Offline - EventBus.baseNodeState.post(BaseNodeState.Offline) - listeners.iterator().forEach { it.onBaseNodeSyncComplete(false) } - } - - } - } - - private fun getUserByWalletAddress(address: TariWalletAddress): TariContact { - val contactsFFI = wallet.getContacts() - for (i in 0 until contactsFFI.getLength()) { - val contactFFI = contactsFFI.getAt(i) - val walletAddressFFI = contactFFI.getWalletAddress() - val tariContact = if (TariWalletAddress(walletAddressFFI) == address) TariContact(contactFFI) else null - walletAddressFFI.destroy() - contactFFI.destroy() - if (tariContact != null) { - contactsFFI.destroy() - return tariContact - } - } - // destroy native collection - contactsFFI.destroy() - return TariContact(address) - } - - fun postTxNotification(tx: Tx) { - txReceivedNotificationDelayedAction?.dispose() - inboundTxEventNotificationTxs.add(tx) - txReceivedNotificationDelayedAction = - Observable.timer(500, TimeUnit.MILLISECONDS) - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.io()) - .subscribe { - // if app is backgrounded, display heads-up notification - val currentActivity = app.currentActivity - if (!app.isInForeground - || currentActivity !is HomeActivity - || !currentActivity.willNotifyAboutNewTx() - ) { - notificationHelper.postCustomLayoutTxNotification(inboundTxEventNotificationTxs.last()) - } - inboundTxEventNotificationTxs.clear() - } - } - - private fun sendPushNotificationToTxRecipient(recipientHex: String) { - val senderHex = wallet.getWalletAddress().notificationHex() - notificationService.notifyRecipient(recipientHex, senderHex, wallet::signMessage) - } - - private fun checkBaseNodeSyncCompletion() { - // make a copy of the status map for concurrency protection - val statusMapCopy = baseNodeValidationStatusMap.toMap() - // if base node not in sync, then switch to the next base node - // check if any has failed - val failed = statusMapCopy.any { it.value.second == false } - if (failed) { - baseNodeValidationStatusMap.clear() - baseNodeSharedPrefsRepository.baseNodeLastSyncResult = false - val currentBaseNode = baseNodeSharedPrefsRepository.currentBaseNode - if (currentBaseNode == null || !currentBaseNode.isCustom) { - baseNodesManager.setNextBaseNode() - baseNodesManager.startSync() - } - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) - listeners.iterator().forEach { it.onBaseNodeSyncComplete(false) } - return - } - // if any of the results is null, we're still waiting for all callbacks to happen - val inProgress = statusMapCopy.any { it.value.second == null } - if (inProgress) { - return - } - // check if it's successful - val successful = statusMapCopy.all { it.value.second == true } - if (successful) { - baseNodeValidationStatusMap.clear() - baseNodeSharedPrefsRepository.baseNodeLastSyncResult = true - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Online) - listeners.iterator().forEach { it.onBaseNodeSyncComplete(true) } - } - // shouldn't ever reach here - no-op - } - - private fun checkValidationResult(type: BaseNodeValidationType, responseId: BigInteger, isSuccess: Boolean) { - try { - val currentStatus = baseNodeValidationStatusMap[type] ?: return - if (currentStatus.first != responseId) return - baseNodeValidationStatusMap[type] = Pair(currentStatus.first, isSuccess) - checkBaseNodeSyncCompletion() - } catch (e: Throwable) { - logger.i(e.toString()) - } - } - - override fun onWalletRestoration(result: WalletRestorationResult) { - EventBus.walletRestorationState.post(result) - } - - override fun onBaseNodeStateChanged(baseNodeState: FFITariBaseNodeState) { - baseNodesManager.saveBaseNodeState(baseNodeState) - } - - override fun onWalletScannedHeight(height: Int) { - baseNodesManager.saveWalletScannedHeight(height) - } - - enum class ConnectivityStatus(val value: Int) { - CONNECTING(0), - ONLINE(1), - OFFLINE(2), - } - - data class OutboundTxNotification(val txId: BigInteger, val recipientPublicKeyHex: String) -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubImpl.kt b/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubImpl.kt index 49cd6b694..7f0b0dd2d 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubImpl.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubImpl.kt @@ -1,16 +1,13 @@ package com.tari.android.wallet.service.service -import com.orhanobut.logger.Logger -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository -import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.application.walletManager.OutboundTxNotifier +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.ffi.Base58String import com.tari.android.wallet.ffi.FFIContact import com.tari.android.wallet.ffi.FFIError import com.tari.android.wallet.ffi.FFIException -import com.tari.android.wallet.ffi.FFIPublicKey import com.tari.android.wallet.ffi.FFITariWalletAddress import com.tari.android.wallet.ffi.FFIWallet -import com.tari.android.wallet.ffi.HexString import com.tari.android.wallet.ffi.runWithDestroy import com.tari.android.wallet.model.BalanceInfo import com.tari.android.wallet.model.CancelledTx @@ -26,23 +23,16 @@ import com.tari.android.wallet.model.TariVector import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.WalletError -import com.tari.android.wallet.model.fullBase58 import com.tari.android.wallet.service.TariWalletService -import com.tari.android.wallet.service.TariWalletServiceListener -import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.util.Constants import java.math.BigInteger import java.util.Locale class TariWalletServiceStubImpl( private val wallet: FFIWallet, - private val baseNodeSharedPrefsRepository: BaseNodePrefRepository, - private val walletServiceListener: FFIWalletListenerImpl + private val outboundTxNotifier: OutboundTxNotifier, ) : TariWalletService.Stub() { - private val logger - get() = Logger.t(WalletService::class.simpleName) - private var _cachedTariContacts: List? = null private val cachedTariContacts: List @Synchronized get() { @@ -60,16 +50,6 @@ class TariWalletServiceStubImpl( return tariContacts.sortedWith(compareBy { it.alias }).also { _cachedTariContacts = it } } - override fun registerListener(listener: TariWalletServiceListener): Boolean { - walletServiceListener.listeners.add(listener) - listener.asBinder().linkToDeath({ walletServiceListener.listeners.remove(listener) }, 0) - return true - } - - override fun unregisterListener(listener: TariWalletServiceListener): Boolean = walletServiceListener.listeners.remove(listener) - - override fun getWalletAddressBase58(error: WalletError): String? = runMapping(error) { wallet.getWalletAddress().fullBase58() } - override fun getBalanceInfo(error: WalletError): BalanceInfo? = runMapping(error) { wallet.getBalance() } override fun estimateTxFee(amount: MicroTari, error: WalletError, feePerGram: MicroTari?): MicroTari? = runMapping(error) { @@ -148,30 +128,6 @@ class TariWalletServiceStubImpl( override fun cancelPendingTx(id: TxId, error: WalletError): Boolean = runMapping(error) { wallet.cancelPendingTx(id.value) } ?: false - override fun addBaseNodePeer(baseNodePublicKey: String, baseNodeAddress: String, error: WalletError): Boolean = runMapping(error) { - Logger.t(this::class.simpleName).e("walletServiceStub:addBaseNodePeer:publicKeyHex: $baseNodePublicKey") - Logger.t(this::class.simpleName).e("walletServiceStub:addBaseNodePeer:address: $baseNodeAddress") - val result = FFIPublicKey(HexString(baseNodePublicKey)).runWithDestroy { wallet.addBaseNodePeer(it, baseNodeAddress) } - if (result) { - walletServiceListener.baseNodeValidationStatusMap.clear() - EventBus.baseNodeSyncState.post(BaseNodeSyncState.NotStarted) - } - result - } ?: false - - override fun startBaseNodeSync(error: WalletError): Boolean = runMapping(error, { - logger.i(it.toString() + "Base node sync failed") - baseNodeSharedPrefsRepository.baseNodeLastSyncResult = false - walletServiceListener.baseNodeValidationStatusMap.clear() - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) - }) { - walletServiceListener.baseNodeValidationStatusMap.clear() - walletServiceListener.baseNodeValidationStatusMap[BaseNodeValidationType.TXO] = Pair(wallet.startTXOValidation(), null) - walletServiceListener.baseNodeValidationStatusMap[BaseNodeValidationType.TX] = Pair(wallet.startTxValidation(), null) - baseNodeSharedPrefsRepository.baseNodeLastSyncResult = null - true - } ?: false - override fun sendTari( tariContact: TariContact, amount: MicroTari, @@ -184,8 +140,8 @@ class TariWalletServiceStubImpl( val recipientAddress = FFITariWalletAddress(Base58String(tariContact.walletAddress.fullBase58)) val txId = wallet.sendTx(recipientAddress, amount.value, feePerGram.value, message, isOneSidePayment, paymentId) - walletServiceListener.outboundTxIdsToBePushNotified.add( - FFIWalletListenerImpl.OutboundTxNotification( + outboundTxNotifier.outboundTxIdsToBePushNotified.add( + WalletManager.OutboundTxNotification( txId = txId, recipientPublicKeyHex = recipientAddress.notificationHex().lowercase(Locale.ENGLISH), ) @@ -226,12 +182,6 @@ class TariWalletServiceStubImpl( } } ?: false - override fun getWalletAddressFromEmojiId(emojiId: String?, error: WalletError): TariWalletAddress? = - runMapping(error) { FFITariWalletAddress(emojiId = emojiId.orEmpty()).runWithDestroy { TariWalletAddress(it) } } - - override fun getWalletAddressFromBase58(walletAddressBase58: String?, error: WalletError): TariWalletAddress? = - runMapping(error) { FFITariWalletAddress(base58 = Base58String(walletAddressBase58 ?: "")).runWithDestroy { TariWalletAddress(it) } } - override fun setKeyValue(key: String, value: String, error: WalletError): Boolean = runMapping(error) { wallet.setKeyValue(key, value) } ?: false override fun getKeyValue(key: String, error: WalletError): String? = runMapping(error) { wallet.getKeyValue(key) } diff --git a/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubProxy.kt b/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubProxy.kt index e0432b8c8..783770922 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubProxy.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/TariWalletServiceStubProxy.kt @@ -15,7 +15,6 @@ import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.WalletError import com.tari.android.wallet.service.TariWalletService -import com.tari.android.wallet.service.TariWalletServiceListener class TariWalletServiceStubProxy : TariWalletService.Stub() { @@ -27,12 +26,6 @@ class TariWalletServiceStubProxy : TariWalletService.Stub() { _stub = newStub } - override fun registerListener(listener: TariWalletServiceListener): Boolean = stub?.registerListener(listener) ?: false - - override fun unregisterListener(listener: TariWalletServiceListener): Boolean = stub?.unregisterListener(listener) ?: false - - override fun getWalletAddressBase58(error: WalletError): String? = stub?.getWalletAddressBase58(error) - override fun getBalanceInfo(error: WalletError): BalanceInfo? = stub?.getBalanceInfo(error) override fun estimateTxFee(amount: MicroTari, error: WalletError, feePerGram: MicroTari?): MicroTari? = @@ -58,11 +51,6 @@ class TariWalletServiceStubProxy : TariWalletService.Stub() { override fun cancelPendingTx(id: TxId, error: WalletError): Boolean = stub?.cancelPendingTx(id, error) ?: false - override fun addBaseNodePeer(baseNodePublicKey: String, baseNodeAddress: String, error: WalletError): Boolean = - stub?.addBaseNodePeer(baseNodePublicKey, baseNodeAddress, error) ?: false - - override fun startBaseNodeSync(error: WalletError): Boolean = stub?.startBaseNodeSync(error) ?: false - override fun sendTari( contact: TariContact, amount: MicroTari, @@ -79,11 +67,6 @@ class TariWalletServiceStubProxy : TariWalletService.Stub() { override fun removeContact(contactPublicKey: TariWalletAddress, error: WalletError): Boolean = stub?.removeContact(contactPublicKey, error) ?: false - override fun getWalletAddressFromBase58(base58: String, error: WalletError): TariWalletAddress? = stub?.getWalletAddressFromBase58(base58, error) - - override fun getWalletAddressFromEmojiId(emojiId: String, error: WalletError): TariWalletAddress? = - stub?.getWalletAddressFromEmojiId(emojiId, error) - override fun setKeyValue(key: String, value: String, error: WalletError): Boolean = stub?.setKeyValue(key, value, error) ?: false override fun getKeyValue(key: String, error: WalletError): String? = stub?.getKeyValue(key, error) diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt index b74300073..016a52353 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt @@ -40,18 +40,15 @@ import android.os.Looper import androidx.lifecycle.ProcessLifecycleOwner import com.orhanobut.logger.Logger import com.tari.android.wallet.application.TariWalletApplication -import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.application.walletManager.WalletManager -import com.tari.android.wallet.application.walletManager.WalletStateHandler +import com.tari.android.wallet.application.walletManager.doOnWalletStarted import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository import com.tari.android.wallet.di.DiContainer import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.infrastructure.backup.BackupManager import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.service.ServiceRestartBroadcastReceiver -import com.tari.android.wallet.service.notification.NotificationService import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.startAction import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.stopAction import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.stopAndDeleteAction @@ -88,30 +85,18 @@ class WalletService : Service() { @Inject lateinit var resourceManager: ResourceManager - @Inject - lateinit var notificationService: NotificationService - @Inject lateinit var notificationHelper: NotificationHelper @Inject lateinit var sharedPrefsWrapper: CorePrefRepository - @Inject - lateinit var baseNodeSharedPrefsRepository: BaseNodePrefRepository - @Inject lateinit var walletManager: WalletManager @Inject lateinit var backupManager: BackupManager - @Inject - lateinit var baseNodesManager: BaseNodesManager - - @Inject - lateinit var walletStateHandler: WalletStateHandler - private var lifecycleObserver: ServiceLifecycleCallbacks? = null private val stubProxy = TariWalletServiceStubProxy() @@ -120,7 +105,7 @@ class WalletService : Service() { private lateinit var wallet: FFIWallet private val job = SupervisorJob() - private val scope = CoroutineScope(Dispatchers.IO + job) + private val serviceScope = CoroutineScope(Dispatchers.IO + job) private val logger get() = Logger.t(WalletService::class.simpleName) @@ -164,8 +149,8 @@ class WalletService : Service() { //todo total crutch. Service is auto-creating during the bind func. Need to refactor this first DiContainer.appComponent.inject(this) - scope.launch { - walletStateHandler.doOnWalletStarted { + serviceScope.launch { + walletManager.doOnWalletStarted { onWalletStarted(it) } } @@ -196,17 +181,10 @@ class WalletService : Service() { private fun onWalletStarted(ffiWallet: FFIWallet) { wallet = ffiWallet lifecycleObserver = ServiceLifecycleCallbacks(wallet) - val impl = FFIWalletListenerImpl( + stubProxy.stub = TariWalletServiceStubImpl( wallet = wallet, - backupManager = backupManager, - notificationHelper = notificationHelper, - notificationService = notificationService, - app = app, - baseNodeSharedPrefsRepository = baseNodeSharedPrefsRepository, - baseNodesManager = baseNodesManager, + outboundTxNotifier = walletManager, ) - stubProxy.stub = TariWalletServiceStubImpl(wallet, baseNodeSharedPrefsRepository, impl) - wallet.listener = impl scheduleExpirationCheck() Handler(Looper.getMainLooper()).post { ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleObserver!!) } walletManager.onWalletStarted() @@ -298,7 +276,7 @@ class WalletService : Service() { object KeyValueStorageKeys { const val NETWORK = "SU7FM2O6Q3BU4XVN7HDD" - const val version = "version" + const val VERSION = "version" } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/CommonViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/common/CommonViewModel.kt index 7eeb7b9e7..917bbce85 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/CommonViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/CommonViewModel.kt @@ -4,11 +4,12 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import com.orhanobut.logger.Logger import com.orhanobut.logger.Printer import com.tari.android.wallet.R -import com.tari.android.wallet.application.walletManager.WalletStateHandler +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.application.walletManager.doOnWalletFailed +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.security.SecurityPrefRepository @@ -17,6 +18,7 @@ import com.tari.android.wallet.di.ApplicationComponent import com.tari.android.wallet.di.DiContainer import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.addTo +import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.infrastructure.logging.LoggerTags @@ -42,9 +44,6 @@ import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.AllSetting import com.tari.android.wallet.ui.fragment.home.navigation.TariNavigator import com.tari.android.wallet.ui.fragment.settings.themeSelector.TariTheme import io.reactivex.disposables.CompositeDisposable -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import javax.inject.Inject open class CommonViewModel : ViewModel() { @@ -81,7 +80,7 @@ open class CommonViewModel : ViewModel() { get() = serviceConnection.walletService @Inject - lateinit var walletStateHandler: WalletStateHandler + lateinit var walletManager: WalletManager @Inject lateinit var dialogManager: DialogManager @@ -127,8 +126,8 @@ open class CommonViewModel : ViewModel() { checkAuthorization() }.addTo(compositeDisposable) - viewModelScope.launch { - walletStateHandler.doOnWalletFailed { + launchOnIo { + walletManager.doOnWalletFailed { showErrorDialog(it) } } @@ -143,14 +142,14 @@ open class CommonViewModel : ViewModel() { } fun doOnWalletServiceConnected(action: suspend (walletService: TariWalletService) -> Unit) { - viewModelScope.launch { + launchOnIo { serviceConnection.doOnWalletServiceConnected(action) } } fun doOnWalletRunning(action: suspend (walletService: FFIWallet) -> Unit) { - viewModelScope.launch { - walletStateHandler.doOnWalletRunning(action) + launchOnIo { + walletManager.doOnWalletRunning(action) } } @@ -176,8 +175,6 @@ open class CommonViewModel : ViewModel() { } } - fun doOnBackground(action: suspend CoroutineScope.() -> Unit): Job = viewModelScope.launch { action() } - fun showModularDialog(args: ModularDialogArgs) { _modularDialog.postValue(args) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/ContactsRepository.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/ContactsRepository.kt index ed90e5e6b..d4432ac60 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/ContactsRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/ContactsRepository.kt @@ -2,7 +2,7 @@ package com.tari.android.wallet.ui.fragment.contactBook.data import android.content.Context import com.orhanobut.logger.Logger -import com.tari.android.wallet.application.walletManager.WalletStateHandler +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.di.ApplicationScope import com.tari.android.wallet.extension.replaceItem import com.tari.android.wallet.model.TariWalletAddress @@ -29,7 +29,7 @@ class ContactsRepository @Inject constructor( context: Context, contactUtil: ContactUtil, tariWalletServiceConnection: TariWalletServiceConnection, - walletStateHandler: WalletStateHandler, + walletManager: WalletManager, @ApplicationScope private val applicationScope: CoroutineScope, ) { private val logger @@ -38,7 +38,7 @@ class ContactsRepository @Inject constructor( private val ffiBridge = FFIContactsRepositoryBridge( contactsRepository = this, tariWalletServiceConnection = tariWalletServiceConnection, - walletStateHandler = walletStateHandler, + walletManager = walletManager, contactUtil = contactUtil, externalScope = applicationScope, ) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/FFIContactsRepositoryBridge.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/FFIContactsRepositoryBridge.kt index 4aa227905..b1ccbfc88 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/FFIContactsRepositoryBridge.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/data/FFIContactsRepositoryBridge.kt @@ -1,9 +1,10 @@ package com.tari.android.wallet.ui.fragment.contactBook.data import com.orhanobut.logger.Logger -import com.tari.android.wallet.application.walletManager.WalletStateHandler -import com.tari.android.wallet.event.Event -import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent +import com.tari.android.wallet.application.walletManager.doOnWalletRunning +import com.tari.android.wallet.application.walletManager.doOnWalletRunningWithValue import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.WalletError import com.tari.android.wallet.service.connection.TariWalletServiceConnection @@ -17,7 +18,7 @@ import kotlinx.coroutines.launch class FFIContactsRepositoryBridge( private val contactsRepository: ContactsRepository, private val tariWalletServiceConnection: TariWalletServiceConnection, - private val walletStateHandler: WalletStateHandler, + private val walletManager: WalletManager, private val contactUtil: ContactUtil, private val externalScope: CoroutineScope, ) { @@ -26,19 +27,22 @@ class FFIContactsRepositoryBridge( init { externalScope.launch { - walletStateHandler.doOnWalletRunning { - tariWalletServiceConnection.doOnWalletServiceConnected { - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } - EventBus.subscribe(this) { contactsRepository.onNewTxReceived() } + walletManager.walletEvent.collect { event -> + when (event) { + is WalletEvent.Tx.TxReceived, + is WalletEvent.Tx.TxReplyReceived, + is WalletEvent.Tx.TxFinalized, + is WalletEvent.Tx.InboundTxBroadcast, + is WalletEvent.Tx.OutboundTxBroadcast, + is WalletEvent.Tx.TxMinedUnconfirmed, + is WalletEvent.Tx.TxMined, + is WalletEvent.Tx.TxFauxMinedUnconfirmed, + is WalletEvent.Tx.TxFauxConfirmed, + is WalletEvent.Tx.TxCancelled -> contactsRepository.onNewTxReceived() + + else -> Unit } + } } } @@ -46,7 +50,7 @@ class FFIContactsRepositoryBridge( suspend fun updateToFFI(contacts: List) { logger.i("Contacts repository event: Saving updates to FFI") - walletStateHandler.doOnWalletRunning { + walletManager.doOnWalletRunning { tariWalletServiceConnection.doOnWalletServiceConnected { service -> contacts.mapNotNull { it.getFFIContactInfo() }.forEach { ffiContactInfo -> val error = WalletError() @@ -89,7 +93,7 @@ class FFIContactsRepositoryBridge( private suspend fun loadFFIContacts(): List { val walletContacts: List = try { - walletStateHandler.doOnWalletRunningWithValue { walletService -> + walletManager.doOnWalletRunningWithValue { walletService -> walletService.getContacts().items() .map { ffiContact -> FFIContactInfo( @@ -142,7 +146,7 @@ class FFIContactsRepositoryBridge( suspend fun deleteContact(contact: FFIContactInfo) { logger.i("Contacts repository event: Deleting contact from FFI") - walletStateHandler.doOnWalletRunning { + walletManager.doOnWalletRunning { tariWalletServiceConnection.doOnWalletServiceConnected { service -> val error = WalletError() service.removeContact(contact.walletAddress, error) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 84a9fd70f..6615d8c4e 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -8,11 +8,11 @@ import com.tari.android.wallet.R import com.tari.android.wallet.application.YatAdapter import com.tari.android.wallet.application.YatAdapter.ConnectedWallet import com.tari.android.wallet.application.deeplinks.DeepLink +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository import com.tari.android.wallet.event.Event import com.tari.android.wallet.event.EventBus -import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.Tx @@ -103,6 +103,7 @@ import javax.inject.Singleton class TariNavigator @Inject constructor( val prefs: CorePrefRepository, val tariSettingsSharedRepository: TariSettingsPrefRepository, + val walletManager: WalletManager, private val yatAdapter: YatAdapter, ) { @@ -355,7 +356,7 @@ class TariNavigator @Inject constructor( } private fun sendToUserByDeeplink(deeplink: DeepLink.Send) { - FFIWallet.instance?.getWalletAddress() + walletManager.walletInstance?.getWalletAddress() // TODO move all the logic beside of navigation from here val address = TariWalletAddress.fromBase58(deeplink.walletAddress) val contact = (activity as HomeActivity).viewModel.contactsRepository.getContactByAddress(address) val bundle = Bundle().apply { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt index ef3a93175..48edfba67 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt @@ -40,7 +40,8 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.lifecycleScope import com.tari.android.wallet.R -import com.tari.android.wallet.application.walletManager.WalletStateHandler +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.application.walletManager.doOnWalletFailed import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.databinding.ActivityOnboardingFlowBinding @@ -82,7 +83,7 @@ class OnboardingFlowActivity : CommonActivity() { - @Inject - lateinit var walletStateHandler: WalletStateHandler - private val uiHandler = Handler(Looper.getMainLooper()) private var emojiIdContinueButtonHasBeenDisplayed = false diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/createWallet/CreateWalletViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/createWallet/CreateWalletViewModel.kt index c04629726..ff9bcb989 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/createWallet/CreateWalletViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/createWallet/CreateWalletViewModel.kt @@ -1,13 +1,13 @@ package com.tari.android.wallet.ui.fragment.onboarding.createWallet -import androidx.lifecycle.viewModelScope +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.event.EffectChannelFlow +import com.tari.android.wallet.extension.launchOnIo +import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.fragment.onboarding.createWallet.CreateWalletModel.Effect -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch import javax.inject.Inject class CreateWalletViewModel : CommonViewModel() { @@ -28,9 +28,9 @@ class CreateWalletViewModel : CommonViewModel() { } fun waitUntilWalletCreated() { - viewModelScope.launch(Dispatchers.IO) { - walletStateHandler.doOnWalletRunning { - viewModelScope.launch(Dispatchers.Main) { + launchOnIo { + walletManager.doOnWalletRunning { + launchOnMain { _effect.send(Effect.StartCheckmarkAnimation) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/localAuth/LocalAuthViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/localAuth/LocalAuthViewModel.kt index 2ad13f45d..5e5429b9d 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/localAuth/LocalAuthViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/localAuth/LocalAuthViewModel.kt @@ -1,8 +1,10 @@ package com.tari.android.wallet.ui.fragment.onboarding.localAuth import androidx.lifecycle.viewModelScope +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.event.EffectChannelFlow import com.tari.android.wallet.extension.addTo +import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.infrastructure.backup.BackupManager import com.tari.android.wallet.infrastructure.security.biometric.BiometricAuthenticationService import com.tari.android.wallet.ui.common.CommonViewModel @@ -10,7 +12,6 @@ import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthModel.Effect import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthModel.SecureState import com.tari.android.wallet.ui.fragment.pinCode.PinCodeScreenBehavior -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -49,12 +50,12 @@ class LocalAuthViewModel : CommonViewModel() { fun proceedToMain() { viewModelScope.launch { - walletStateHandler.doOnWalletRunning { + walletManager.doOnWalletRunning { securityPrefRepository.isAuthenticated = true sharedPrefsRepository.onboardingAuthSetupCompleted = true backupManager.backupNow() - navigation.postValue(Navigation.EnterPinCodeNavigation(PinCodeScreenBehavior.CreateConfirm)) - viewModelScope.launch(Dispatchers.Main) { + tariNavigator.navigate(Navigation.EnterPinCodeNavigation(PinCodeScreenBehavior.CreateConfirm)) + launchOnMain { _effect.send(Effect.OnAuthSuccess) } } @@ -62,7 +63,7 @@ class LocalAuthViewModel : CommonViewModel() { } fun goToEnterPinCode() { - navigation.postValue(Navigation.EnterPinCodeNavigation(PinCodeScreenBehavior.Create)) + tariNavigator.navigate(Navigation.EnterPinCodeNavigation(PinCodeScreenBehavior.Create)) } private fun updateState() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index 8314a8662..bb5ac0034 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -5,8 +5,11 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.doOnWalletFailed +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.backup.BackupPrefRepository +import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.infrastructure.backup.BackupFileIsEncryptedException import com.tari.android.wallet.infrastructure.backup.BackupManager import com.tari.android.wallet.infrastructure.backup.BackupStorageAuthRevokedException @@ -52,11 +55,11 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { options.postValue(backupPrefRepository.getOptionList) - viewModelScope.launch(Dispatchers.IO) { - walletStateHandler.doOnWalletRunning { + launchOnIo { + walletManager.doOnWalletRunning { if (WalletUtil.walletExists(walletConfig) && state.value != null) { backupPrefRepository.restoredTxs?.let { - if (it.utxos.orEmpty().isEmpty()) return@let + if (it.utxos.isEmpty()) return@let val tariWalletAddress = TariWalletAddress.fromBase58(it.sourceBase58) val message = resourceManager.getString(R.string.backup_restored_tx) @@ -74,8 +77,8 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } } - viewModelScope.launch(Dispatchers.IO) { - walletStateHandler.doOnWalletFailed { + launchOnIo { + walletManager.doOnWalletFailed { handleException(WalletStartFailedException(it)) } } @@ -86,7 +89,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { try { if (backupManager.onSetupActivityResult(requestCode, resultCode, data)) { restoreFromBackup() diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt index c192f1020..a793e77a4 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt @@ -1,10 +1,13 @@ package com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword import androidx.lifecycle.LiveData -import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.doOnWalletFailed +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.backup.BackupPrefRepository +import com.tari.android.wallet.extension.launchOnIo +import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.infrastructure.backup.BackupManager import com.tari.android.wallet.infrastructure.backup.WalletStartFailedException import com.tari.android.wallet.service.service.WalletServiceLauncher @@ -13,8 +16,6 @@ import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.util.WalletUtil -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.security.GeneralSecurityException import javax.inject.Inject @@ -35,8 +36,8 @@ class EnterRestorationPasswordViewModel : CommonViewModel() { init { component.inject(this) - viewModelScope.launch { - walletStateHandler.doOnWalletRunning { + launchOnIo { + walletManager.doOnWalletRunning { if (WalletUtil.walletExists(walletConfig)) { val dto = backupSettingsRepository.getOptionDto(backupManager.currentOption!!)!!.copy(isEnable = true) backupSettingsRepository.updateOption(dto) @@ -47,8 +48,8 @@ class EnterRestorationPasswordViewModel : CommonViewModel() { } } - viewModelScope.launch { - walletStateHandler.doOnWalletFailed { + launchOnIo { + walletManager.doOnWalletFailed { handleRestorationFailure(WalletStartFailedException(it)) } } @@ -59,7 +60,7 @@ class EnterRestorationPasswordViewModel : CommonViewModel() { fun onBack() { backPressed.postValue(Unit) - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { backupManager.signOut() } } @@ -70,11 +71,11 @@ class EnterRestorationPasswordViewModel : CommonViewModel() { } private fun performRestoration(password: String) { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { try { backupManager.restoreLatestBackup(password) backupSettingsRepository.backupPassword = password - viewModelScope.launch(Dispatchers.Main) { + launchOnMain { walletServiceLauncher.start() } } catch (exception: Throwable) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt index c8cb1c60a..805df0f29 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt @@ -158,7 +158,7 @@ class InputSeedWordsFragment : CommonFragment = _inProgress val isAllEntered: LiveData = _words.map { words -> - words.all { it.text.value!!.isNotEmpty() } && words.size == SeedPhrase.SeedPhraseLength + words.all { it.text.value!!.isNotEmpty() } && words.size == SeedPhrase.SEED_PHRASE_LENGTH } private val _continueButtonState = MediatorLiveData() @@ -109,7 +110,7 @@ class InputSeedWordsViewModel : CommonViewModel() { } private fun loadSuggestions() { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { val mnemonic = FFISeedWords.getMnemomicWordList(FFISeedWords.Language.English) val size = mnemonic.getLength() for (i in 0 until size) { @@ -121,8 +122,8 @@ class InputSeedWordsViewModel : CommonViewModel() { private fun startRestoring() { _inProgress.postValue(true) - viewModelScope.launch(Dispatchers.IO) { - walletStateHandler.doOnWalletFailed { exception -> + launchOnIo { + walletManager.doOnWalletFailed { exception -> if (WalletError(exception) == WalletError.NoError) { onError(RestorationError.Unknown(resourceManager)) } else { @@ -133,8 +134,8 @@ class InputSeedWordsViewModel : CommonViewModel() { } } - viewModelScope.launch(Dispatchers.IO) { - walletStateHandler.doOnWalletRunning { + launchOnIo { + walletManager.doOnWalletRunning { navigation.postValue(Navigation.InputSeedWordsNavigation.ToRestoreFormSeedWordsInProgress) _inProgress.postValue(false) } @@ -143,6 +144,7 @@ class InputSeedWordsViewModel : CommonViewModel() { customBaseNodeState.value.customBaseNode?.let { baseNodesManager.addUserBaseNode(it) baseNodesManager.setBaseNode(it) + walletManager.syncBaseNode() } walletServiceLauncher.start() } @@ -208,7 +210,7 @@ class InputSeedWordsViewModel : CommonViewModel() { list[index].text.value = formattedText[0] for ((formattedIndex, item) in formattedText.drop(1).withIndex()) { val nextIndex = index + formattedIndex + 1 - if (list.size < SeedPhrase.SeedPhraseLength) { + if (list.size < SeedPhrase.SEED_PHRASE_LENGTH) { addWord(nextIndex, item) } } @@ -225,7 +227,7 @@ class InputSeedWordsViewModel : CommonViewModel() { fun getFocusToNextElement(currentIndex: Int) { val list = _words.value!! - if (list.isEmpty() || list.last().text.value!!.isNotEmpty() && list.size < SeedPhrase.SeedPhraseLength) { + if (list.isEmpty() || list.last().text.value!!.isNotEmpty() && list.size < SeedPhrase.SEED_PHRASE_LENGTH) { WordItemViewModel.create("", mnemonicList).apply { list.add(this) reindex() @@ -250,7 +252,7 @@ class InputSeedWordsViewModel : CommonViewModel() { fun finishEntering(index: Int, text: String) { val list = _words.value!! - if (text.isNotEmpty() && list.size < SeedPhrase.SeedPhraseLength) { + if (text.isNotEmpty() && list.size < SeedPhrase.SEED_PHRASE_LENGTH) { addWord(index + 1) } _words.value = _words.value @@ -275,10 +277,10 @@ class InputSeedWordsViewModel : CommonViewModel() { fun selectSuggestion(suggestionViewHolderItem: SuggestionViewHolderItem) { onCurrentWordChanges(_focusedIndex.value!!, suggestionViewHolderItem.suggestion + " ") - if (_words.value.orEmpty().size == SeedPhrase.SeedPhraseLength) { - viewModelScope.launch(Dispatchers.IO) { + if (_words.value.orEmpty().size == SeedPhrase.SEED_PHRASE_LENGTH) { + launchOnIo { delay(100) - viewModelScope.launch(Dispatchers.Main) { + launchOnMain { finishEntering() } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt index 262930aba..564f9ff4c 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt @@ -9,10 +9,8 @@ import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.addTo import com.tari.android.wallet.ffi.FFIPublicKey -import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.ffi.HexString import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.domain.ResourceManager @@ -26,10 +24,6 @@ import javax.inject.Inject class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { - - @Inject - lateinit var seedPhraseRepository: SeedPhraseRepository - @Inject lateinit var walletServiceLauncher: WalletServiceLauncher @@ -62,7 +56,7 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private fun startRestoringOnNode(baseNode: BaseNodeDto) { try { val baseNodeFFI = FFIPublicKey(HexString(baseNode.publicKeyHex)) - val result = FFIWallet.instance?.startRecovery(baseNodeFFI, resourceManager.getString(R.string.restore_wallet_output_message)) + val result = walletManager.walletInstance?.startRecovery(baseNodeFFI, resourceManager.getString(R.string.restore_wallet_output_message)) if (result == true) { subscribeOnRestorationState() return diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountViewModel.kt index 7adf2cc38..08eafaef3 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountViewModel.kt @@ -2,10 +2,9 @@ package com.tari.android.wallet.ui.fragment.send.addAmount import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R import com.tari.android.wallet.extension.getWithError -import com.tari.android.wallet.ffi.FFIWallet +import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.WalletError @@ -17,8 +16,6 @@ import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.fragment.send.addAmount.feeModule.FeeModule import com.tari.android.wallet.ui.fragment.send.addAmount.feeModule.NetworkSpeed import com.tari.android.wallet.util.Constants -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.math.BigInteger import kotlin.math.min @@ -48,9 +45,9 @@ class AddAmountViewModel : CommonViewModel() { } private fun loadFees() = doOnWalletServiceConnected { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { try { - val stats = FFIWallet.instance!!.getFeePerGramStats() + val stats = walletManager.walletInstance!!.getFeePerGramStats() val elements = (0 until stats.getLength()).map { stats.getAt(it) }.toList() val elementsCount = min(stats.getLength(), 3) @@ -85,7 +82,7 @@ class AddAmountViewModel : CommonViewModel() { } _feePerGrams.postValue(FeePerGramOptions(networkSpeed, MicroTari(slowOption), MicroTari(mediumOption), MicroTari(fastOption))) } catch (e: Throwable) { - logger.i(e.message + "load fees") + logger.i("Error loading fees: ${e.message}") } } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt index fcc411f6c..d76d34c33 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt @@ -8,8 +8,9 @@ import com.tari.android.wallet.R.string.finalize_send_tx_sending_step_2_desc_lin import com.tari.android.wallet.R.string.finalize_send_tx_sending_step_2_desc_line_2 import com.tari.android.wallet.R.string.finalize_send_tx_sending_step_3_desc_line_1 import com.tari.android.wallet.R.string.finalize_send_tx_sending_step_3_desc_line_2 -import com.tari.android.wallet.event.Event +import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.model.TariContact @@ -205,15 +206,16 @@ class FinalizeSendTxViewModel : CommonViewModel() { ) { init { - subscribeToEventBus() + collectFlow(walletManager.walletEvent) { event -> + when (event) { + is WalletManager.WalletEvent.Tx.DirectSendResult -> onDirectSendResult(event.txId, event.status) + else -> Unit + } + } } override fun execute() = Unit - private fun subscribeToEventBus() { - EventBus.subscribe(this) { event -> onDirectSendResult(event.txId, event.status) } - } - private fun onDirectSendResult(txId: TxId, status: TransactionSendStatus) { if (sentTxId.value != txId) { return diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt index 5676ae9b0..abf33b3a6 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt @@ -52,6 +52,7 @@ class ChangeBaseNodeViewModel : CommonViewModel() { fun selectBaseNode(baseNodeDto: BaseNodeDto) { baseNodesManager.setBaseNode(baseNodeDto) + walletManager.syncBaseNode() backPressed.postValue(Unit) } @@ -136,6 +137,7 @@ class ChangeBaseNodeViewModel : CommonViewModel() { baseNodesManager.addUserBaseNode(baseNode) baseNodesManager.setBaseNode(baseNode) + walletManager.syncBaseNode() hideDialog() loadList() } else { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt index cd946028d..a2c0c45d2 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt @@ -2,12 +2,13 @@ package com.tari.android.wallet.ui.fragment.settings.networkSelection import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.doOnWalletNotReady import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.network.TariNetwork import com.tari.android.wallet.di.DiContainer import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.SingleLiveEvent @@ -15,7 +16,6 @@ import com.tari.android.wallet.ui.common.recyclerView.CommonViewHolderItem import com.tari.android.wallet.ui.dialog.confirm.ConfirmDialogArgs import com.tari.android.wallet.ui.fragment.settings.networkSelection.networkItem.NetworkViewHolderItem import com.tari.android.wallet.util.WalletUtil -import kotlinx.coroutines.launch import javax.inject.Inject class NetworkSelectionViewModel : CommonViewModel() { @@ -66,8 +66,8 @@ class NetworkSelectionViewModel : CommonViewModel() { networkRepository.currentNetwork = newNetwork loadData() - viewModelScope.launch { - walletStateHandler.doOnWalletNotReady { + launchOnIo { + walletManager.doOnWalletNotReady { EventBus.clear() DiContainer.reInitContainer() _recreate.postValue(Unit) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt index c2cf2bc2a..e9a9db3b2 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt @@ -3,7 +3,6 @@ package com.tari.android.wallet.ui.fragment.settings.torBridges import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R -import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.deeplinks.DeeplinkHandler import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfigurationList @@ -15,9 +14,9 @@ import com.tari.android.wallet.tor.TorProxyManager import com.tari.android.wallet.tor.TorProxyState import com.tari.android.wallet.tor.TorProxyStateHandler import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.DialogArgs import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs +import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle @@ -38,9 +37,6 @@ class TorBridgesSelectionViewModel : CommonViewModel() { @Inject lateinit var torProxyManager: TorProxyManager - @Inject - lateinit var baseNodesManager: BaseNodesManager - @Inject lateinit var deeplinkHandler: DeeplinkHandler @@ -139,7 +135,7 @@ class TorBridgesSelectionViewModel : CommonViewModel() { torProxyManager.shutdown() subscribeToTorState() torProxyManager.run() - baseNodesManager.startSync() + walletManager.syncBaseNode() } private fun subscribeToTorState() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt index b9b2ed5f6..19e6e9a8c 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt @@ -38,7 +38,8 @@ import android.os.Bundle import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.coroutineScope -import com.tari.android.wallet.application.walletManager.WalletStateHandler +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.application.walletManager.doOnWalletNotReady import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository @@ -76,7 +77,7 @@ class SplashActivity : AppCompatActivity() { lateinit var walletServiceLauncher: WalletServiceLauncher @Inject - lateinit var walletStateHandler: WalletStateHandler + lateinit var walletManager: WalletManager override fun onCreate(savedInstanceState: Bundle?) { appComponent.inject(this) @@ -109,7 +110,7 @@ class SplashActivity : AppCompatActivity() { networkRepository.setDefaultNetworkAsCurrent() lifecycle.coroutineScope.launch { - walletStateHandler.doOnWalletNotReady { + walletManager.doOnWalletNotReady { EventBus.clear() DiContainer.reInitContainer() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt index 56003a682..5ff3be552 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt @@ -7,7 +7,6 @@ import android.text.Spanned import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R import com.tari.android.wallet.R.string.error_no_connection_description import com.tari.android.wallet.R.string.error_no_connection_title @@ -15,6 +14,7 @@ import com.tari.android.wallet.R.string.error_node_unreachable_description import com.tari.android.wallet.R.string.error_node_unreachable_title import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager.StagedSecurityEffect +import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.data.sharedPrefs.securityStages.WalletSecurityStage import com.tari.android.wallet.data.sharedPrefs.sentry.SentryPrefRepository @@ -46,8 +46,6 @@ import com.tari.android.wallet.ui.fragment.tx.adapter.TransactionItem import com.tari.android.wallet.util.EmojiId import com.tari.android.wallet.util.extractEmojis import com.tari.android.wallet.util.shortString -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import yat.android.ui.extension.HtmlHelper import javax.inject.Inject @@ -155,9 +153,24 @@ class HomeFragmentViewModel : CommonViewModel() { private fun onServiceConnected() { subscribeToEventBus() - viewModelScope.launch(Dispatchers.IO) { - refreshAllData(true) + collectFlow(walletManager.walletEvent) { event -> + when (event) { + is WalletEvent.Tx.TxReceived, + is WalletEvent.Tx.TxReplyReceived, + is WalletEvent.Tx.TxFinalized, + is WalletEvent.Tx.InboundTxBroadcast, + is WalletEvent.Tx.OutboundTxBroadcast, + is WalletEvent.Tx.TxMinedUnconfirmed, + is WalletEvent.Tx.TxMined, + is WalletEvent.Tx.TxFauxMinedUnconfirmed, + is WalletEvent.Tx.TxFauxConfirmed, + is WalletEvent.Tx.TxCancelled -> refreshBalance(false) + + else -> Unit + } } + + refreshAllData(true) } private fun fetchBalanceInfoData() { @@ -178,14 +191,14 @@ class HomeFragmentViewModel : CommonViewModel() { } private fun refreshAllData(isRestarted: Boolean = false) { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { refreshBalance(isRestarted) transactionRepository.refreshAllData() } } private fun refreshBalance(isRestarted: Boolean = false) { - viewModelScope.launch(Dispatchers.IO) { + launchOnIo { fetchBalanceInfoData() _refreshBalanceInfo.postValue(isRestarted) } @@ -193,16 +206,6 @@ class HomeFragmentViewModel : CommonViewModel() { private fun subscribeToEventBus() { EventBus.subscribe(this) { refreshAllData() } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } - EventBus.subscribe(this) { refreshBalance(false) } EventBus.subscribe(this) { refreshBalance(false) } EventBus.subscribe(this) { onTxSendFailed(it.failureReason) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/TransactionRepository.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/TransactionRepository.kt index 057c8f13b..6ddadaee9 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/TransactionRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/TransactionRepository.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent import com.tari.android.wallet.event.Event import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.collectFlow @@ -80,6 +81,22 @@ class TransactionRepository @Inject constructor() : CommonViewModel() { collectFlow(contactsRepository.contactList) { _listUpdateTrigger.postValue(Unit) } + collectFlow(walletManager.walletEvent) { event -> + when (event) { + is WalletEvent.Tx.TxReceived -> onTxReceived(event.tx) + is WalletEvent.Tx.TxReplyReceived -> onTxReplyReceived(event.tx) + is WalletEvent.Tx.TxFinalized -> onTxFinalized(event.tx) + is WalletEvent.Tx.InboundTxBroadcast -> onInboundTxBroadcast(event.tx) + is WalletEvent.Tx.OutboundTxBroadcast -> onOutboundTxBroadcast(event.tx) + is WalletEvent.Tx.TxMinedUnconfirmed -> onTxMinedUnconfirmed(event.tx) + is WalletEvent.Tx.TxMined -> onTxMined(event.tx) + is WalletEvent.Tx.TxFauxMinedUnconfirmed -> onTxFauxMinedUnconfirmed(event.tx) + is WalletEvent.Tx.TxFauxConfirmed -> onFauxTxMined(event.tx) + is WalletEvent.Tx.TxCancelled -> onTxCancelled(event.tx) + else -> Unit + } + } + viewModelScope.launch(Dispatchers.IO) { updateTxListData() fetchRequiredConfirmationCount() @@ -93,16 +110,6 @@ class TransactionRepository @Inject constructor() : CommonViewModel() { private fun subscribeToEventBus() { EventBus.subscribe(this) { refreshAllData() } - EventBus.subscribe(this) { onTxReceived(it.tx) } - EventBus.subscribe(this) { onTxReplyReceived(it.tx) } - EventBus.subscribe(this) { onTxFinalized(it.tx) } - EventBus.subscribe(this) { onInboundTxBroadcast(it.tx) } - EventBus.subscribe(this) { onOutboundTxBroadcast(it.tx) } - EventBus.subscribe(this) { onTxMinedUnconfirmed(it.tx) } - EventBus.subscribe(this) { onTxMined(it.tx) } - EventBus.subscribe(this) { onTxFauxMinedUnconfirmed(it.tx) } - EventBus.subscribe(this) { onFauxTxMined(it.tx) } - EventBus.subscribe(this) { onTxCancelled(it.tx) } EventBus.subscribe(this) { onTxSendSuccessful(it.txId) } } @@ -153,7 +160,12 @@ class TransactionRepository @Inject constructor() : CommonViewModel() { val nonPendingTxs = (cancelledTxs + nonMinedUnconfirmedCompletedTxs).toMutableList() nonPendingTxs.sortWith(compareByDescending(Tx::timestamp).thenByDescending { it.id }) if (nonPendingTxs.isNotEmpty()) { - items.add(TitleViewHolderItem(title = resourceManager.getString(R.string.home_completed_transactions_title), isFirst = pendingTxs.isEmpty())) + items.add( + TitleViewHolderItem( + title = resourceManager.getString(R.string.home_completed_transactions_title), + isFirst = pendingTxs.isEmpty(), + ) + ) items.addAll(nonPendingTxs.mapIndexed { index, tx -> TransactionItem( tx = tx, diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsViewModel.kt index 3541d0b1d..f52f67473 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsViewModel.kt @@ -9,13 +9,11 @@ import com.tari.android.wallet.R.string.common_are_you_sure import com.tari.android.wallet.R.string.tx_details_cancel_dialog_cancel import com.tari.android.wallet.R.string.tx_details_cancel_dialog_description import com.tari.android.wallet.R.string.tx_details_cancel_dialog_not_cancel -import com.tari.android.wallet.event.Event -import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.getWithError import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.ffi.FFITxCancellationReason -import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.model.CancelledTx import com.tari.android.wallet.model.CompletedTx import com.tari.android.wallet.model.PendingOutboundTx @@ -64,7 +62,7 @@ class TxDetailsViewModel(savedState: SavedStateHandle) : CommonViewModel() { @Inject lateinit var contactsRepository: ContactsRepository - val requiredConfirmationCount: Long? = FFIWallet.getOrNull { it.getRequiredConfirmationCount() }?.toLong() + val requiredConfirmationCount: Long? = walletManager.walletInstance?.getRequiredConfirmationCount()?.toLong() private var txId: TxId? = savedState.get(TX_ID_EXTRA_KEY) @@ -89,9 +87,20 @@ class TxDetailsViewModel(savedState: SavedStateHandle) : CommonViewModel() { findTxAndUpdateUI() } - observeTxUpdates() - collectFlow(contactsRepository.contactList) { updateContact() } + + collectFlow(walletManager.walletEvent) { event -> + when (event) { + is WalletEvent.Tx.InboundTxBroadcast -> updateTxData(event.tx) + is WalletEvent.Tx.OutboundTxBroadcast -> updateTxData(event.tx) + is WalletEvent.Tx.TxFinalized -> updateTxData(event.tx) + is WalletEvent.Tx.TxMined -> updateTxData(event.tx) + is WalletEvent.Tx.TxMinedUnconfirmed -> updateTxData(event.tx) + is WalletEvent.Tx.TxReplyReceived -> updateTxData(event.tx) + is WalletEvent.Tx.TxCancelled -> updateTxData(event.tx) + else -> Unit + } + } } fun addOrEditContact() = showEditNameInputs() @@ -165,16 +174,6 @@ class TxDetailsViewModel(savedState: SavedStateHandle) : CommonViewModel() { return reason?.let { resourceManager.getString(it) } ?: "" } - private fun observeTxUpdates() { - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - EventBus.subscribe(this) { updateTxData(it.tx) } - } - private fun updateTxData(tx: Tx) { if (tx.id == this.tx.value?.id) { setTxArg(tx) diff --git a/app/src/main/java/com/tari/android/wallet/util/Constants.kt b/app/src/main/java/com/tari/android/wallet/util/Constants.kt index ddc148236..feca9868b 100644 --- a/app/src/main/java/com/tari/android/wallet/util/Constants.kt +++ b/app/src/main/java/com/tari/android/wallet/util/Constants.kt @@ -123,8 +123,6 @@ object Constants { * Wallet constants. */ object Wallet { - const val MAX_NUMBER_OF_ROLLING_LOG_FILES = 2 - const val ROLLING_LOG_FILE_MAX_SIZE_BYTES = 10 * 1024 * 1024 const val DISCOVERY_TIMEOUT_SEC = 20L const val STORE_AND_FORWARD_MESSAGE_DURATION_SEC = 10800L const val EMOJI_FORMATTER_CHUNK_SIZE = 3 From 2a1c26ec777820f157f6e8946f4260139accdc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 10:22:03 +0200 Subject: [PATCH 02/32] Add the copy seeds to clipboard button for debug builds --- .../WriteDownSeedPhraseFragment.kt | 3 ++ .../WriteDownSeedPhraseViewModel.kt | 7 ++++ .../tari/android/wallet/util/DebugConfig.kt | 2 ++ .../fragment_write_down_seed_phrase.xml | 36 ++++++++++++++----- app/src/main/res/values/strings.xml | 1 + 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/writeDownSeedWords/WriteDownSeedPhraseFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/writeDownSeedWords/WriteDownSeedPhraseFragment.kt index 12de2c76b..67a7c8f27 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/writeDownSeedWords/WriteDownSeedPhraseFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/writeDownSeedWords/WriteDownSeedPhraseFragment.kt @@ -53,6 +53,7 @@ import com.tari.android.wallet.ui.extension.setVisible import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.settings.backup.writeDownSeedWords.adapter.PhraseWordsAdapter import com.tari.android.wallet.util.Constants +import com.tari.android.wallet.util.DebugConfig class WriteDownSeedPhraseFragment : CommonFragment() { @@ -91,6 +92,8 @@ class WriteDownSeedPhraseFragment : CommonFragment @@ -60,18 +60,34 @@ app:customFont="medium" /> - + app:layout_constraintStart_toStartOf="parent"> + + + + + + android:paddingVertical="8dp" + tools:listitem="@layout/item_phrase_word" /> @@ -109,6 +126,7 @@ android:layout_marginTop="@dimen/write_down_seed_expand_button_top_margin" android:layout_marginEnd="@dimen/write_down_seed_expand_button_end_margin" android:layout_marginBottom="@dimen/write_down_seed_expand_button_bottom_margin" + android:foreground="?attr/selectableItemBackground" android:src="@drawable/vector_recovery_expand_icon" android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/list_container" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index efed02fda..7f3493a88 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -551,6 +551,7 @@ Verify Seed Phrase I understand that if I lose my recovery seed phrase I will not be able to restore my wallet. Error while getting the wallet seed phrase. Please try again later. + Copy seed phrase Verify Seed Phrase From 734ecdb48c50126a859ac93b181ad8006fa45bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 11:19:38 +0200 Subject: [PATCH 03/32] Add a constructor for creating a wallet (#1218) --- .../walletManager/WalletManager.kt | 15 ++++--- .../com/tari/android/wallet/ffi/FFIWallet.kt | 44 ++++++------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index 1f1017ccb..d92131dba 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -309,13 +309,16 @@ class WalletManager @Inject constructor( if (walletInstance == null) { // store network info in shared preferences if it's a new wallet val isNewInstallation = !WalletUtil.walletExists(walletConfig) + + val passphrase = securityPrefRepository.databasePassphrase.takeIf { !it.isNullOrEmpty() } + ?: corePrefRepository.generateDatabasePassphrase().also { securityPrefRepository.databasePassphrase = it } + walletInstance = FFIWallet( - sharedPrefsRepository = corePrefRepository, - securityPrefRepository = securityPrefRepository, - seedPhraseRepository = seedPhraseRepository, - networkRepository = networkPrefRepository, + tariNetwork = networkPrefRepository.currentNetwork, commsConfig = getCommsConfig(), logPath = walletConfig.getWalletLogFilePath(), + passphrase = passphrase, + seedWords = seedPhraseRepository.getPhrase()?.ffiSeedWords, listener = object : FFIWalletListener { /** * All the callbacks are called on the FFI thread, so we need to switch to the main thread. @@ -502,12 +505,12 @@ class WalletManager @Inject constructor( logger.i("startSync:wallet validation:validation result: $type: $isSuccess") checkBaseNodeSyncCompletion() } catch (e: Throwable) { - logger.i(e.toString()) + logger.i("startSync:wallet validation $type:error: ${e.message}") } } private fun checkBaseNodeSyncCompletion() { - // make a copy of the status map for concurrency protection + // make a copy of the status map for concurrency protection§ val statusMapCopy = walletValidationStatusMap.toMap() // if base node not in sync, then switch to the next base node // check if any has failed diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt index 5807769f2..ada0ad619 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt @@ -33,9 +33,7 @@ package com.tari.android.wallet.ffi import com.tari.android.wallet.BuildConfig -import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository -import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository -import com.tari.android.wallet.data.sharedPrefs.security.SecurityPrefRepository +import com.tari.android.wallet.data.sharedPrefs.network.TariNetwork import com.tari.android.wallet.model.BalanceInfo import com.tari.android.wallet.model.CancelledTx import com.tari.android.wallet.model.CompletedTx @@ -49,7 +47,6 @@ import com.tari.android.wallet.model.TariVector import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import java.math.BigInteger /** @@ -59,13 +56,7 @@ import java.math.BigInteger */ class FFIWallet( - private val sharedPrefsRepository: CorePrefRepository, - private val securityPrefRepository: SecurityPrefRepository, - private val seedPhraseRepository: SeedPhraseRepository, - private val networkRepository: NetworkPrefRepository, - private val commsConfig: FFICommsConfig, - private val logPath: String, - private val listener: FFIWalletListener + private val listener: FFIWalletListener, ) : FFIBase() { companion object { @@ -190,24 +181,17 @@ class FFIWallet( private external fun jniDestroy() - // this acts as a constructor would for a normal class since constructors are not allowed for - // singletons - init { - if (pointer == nullptr) { // so it can only be assigned once for the singleton - init() - } - } - - private fun init() { + constructor( + tariNetwork: TariNetwork, + commsConfig: FFICommsConfig, + logPath: String, + passphrase: String, + seedWords: FFISeedWords?, + listener: FFIWalletListener, + ) : this(listener) { val error = FFIError() logger.i("Pre jniCreate") - var passphrase = securityPrefRepository.databasePassphrase - if (passphrase.isNullOrEmpty()) { - passphrase = sharedPrefsRepository.generateDatabasePassphrase() - securityPrefRepository.databasePassphrase = passphrase - } - try { jniCreate( commsConfig = commsConfig, @@ -216,9 +200,9 @@ class FFIWallet( maxNumberOfRollingLogFiles = MAX_NUMBER_OF_ROLLING_LOG_FILES, rollingLogFileMaxSizeBytes = ROLLING_LOG_FILE_MAX_SIZE_BYTES, passphrase = passphrase, - network = networkRepository.currentNetwork.network.uriComponent, - seedWords = seedPhraseRepository.getPhrase()?.ffiSeedWords, - dnsPeer = networkRepository.currentNetwork.dnsPeer, + network = tariNetwork.network.uriComponent, + seedWords = seedWords, + dnsPeer = tariNetwork.dnsPeer, isDnsSecureOn = IS_DNS_SECURE_ON, this::onTxReceived.name, "(J)V", this::onTxReplyReceived.name, "(J)V", @@ -244,7 +228,7 @@ class FFIWallet( throw e } - logger.i("Post jniCreate with code: %d.", error.code) + logger.i("Post jniCreate with code: ${error.code}.") throwIf(error) } From 87d5181d5c2410696063ab7959fed1aa848d0722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 11:53:41 +0200 Subject: [PATCH 04/32] Create NetworkConnectionStateHandler to handle state instead of EventBus --- .../com/tari/android/wallet/event/EventBus.kt | 6 ---- .../network/NetworkConnectionStateHandler.kt | 21 +++++++++++++ .../network/NetworkConnectionStateReceiver.kt | 30 +++++-------------- .../ConnectionIndicatorViewModel.kt | 6 +++- .../fragment/home/navigation/TariNavigator.kt | 11 +++---- .../fragment/send/addNote/AddNoteFragment.kt | 4 +-- .../fragment/send/addNote/AddNoteViewModel.kt | 7 +++++ .../send/finalize/FinalizeSendTxViewModel.kt | 11 +++---- .../wallet/ui/fragment/tx/HomeFragment.kt | 1 - 9 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateHandler.kt diff --git a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt index 9ee6e959c..cc82067bd 100644 --- a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt +++ b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt @@ -35,7 +35,6 @@ package com.tari.android.wallet.event import com.tari.android.wallet.infrastructure.backup.BackupsState import com.tari.android.wallet.model.BalanceInfo import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.network.NetworkConnectionState import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.service.baseNode.BaseNodeSyncState @@ -52,8 +51,6 @@ object EventBus : GeneralEventBus() { val balanceState = BehaviorEventBus() - val networkConnectionState = BehaviorEventBus() - val backupState = BehaviorEventBus() val baseNodeState = BehaviorEventBus() @@ -68,7 +65,6 @@ object EventBus : GeneralEventBus() { fun unsubscribeAll(subscriber: Any) { EventBus.unsubscribe(subscriber) - networkConnectionState.unsubscribe(subscriber) backupState.unsubscribe(subscriber) baseNodeState.unsubscribe(subscriber) baseNodeSyncState.unsubscribe(subscriber) @@ -77,11 +73,9 @@ object EventBus : GeneralEventBus() { override fun clear() { super.clear() - networkConnectionState.clear() backupState.clear() baseNodeState.clear() baseNodeSyncState.clear() walletRestorationState.clear() } } - diff --git a/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateHandler.kt b/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateHandler.kt new file mode 100644 index 000000000..ab979ca65 --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateHandler.kt @@ -0,0 +1,21 @@ +package com.tari.android.wallet.network + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NetworkConnectionStateHandler @Inject constructor() { + + private val _networkConnectionState = MutableStateFlow(NetworkConnectionState.UNKNOWN) + val networkConnectionState = _networkConnectionState.asStateFlow() + + fun updateState(state: NetworkConnectionState) { + _networkConnectionState.value = state + } + + fun isNetworkConnected(): Boolean { + return networkConnectionState.value == NetworkConnectionState.CONNECTED + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateReceiver.kt b/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateReceiver.kt index 323c11d35..0f880d56a 100644 --- a/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateReceiver.kt +++ b/app/src/main/java/com/tari/android/wallet/network/NetworkConnectionStateReceiver.kt @@ -39,15 +39,14 @@ import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET -import android.os.Build -import androidx.annotation.RequiresApi import com.orhanobut.logger.Logger -import com.tari.android.wallet.event.EventBus import javax.inject.Inject import javax.inject.Singleton @Singleton -class NetworkConnectionStateReceiver @Inject constructor() : BroadcastReceiver() { +class NetworkConnectionStateReceiver @Inject constructor( + private val networkConnectionStateHandler: NetworkConnectionStateHandler, +) : BroadcastReceiver() { private val action = "android.net.conn.CONNECTIVITY_CHANGE" val intentFilter = IntentFilter(action) @@ -55,7 +54,7 @@ class NetworkConnectionStateReceiver @Inject constructor() : BroadcastReceiver() get() = Logger.t(NetworkConnectionStateReceiver::class.simpleName) init { - EventBus.networkConnectionState.post(NetworkConnectionState.UNKNOWN) + networkConnectionStateHandler.updateState(NetworkConnectionState.UNKNOWN) } override fun onReceive(context: Context?, intent: Intent?) { @@ -65,29 +64,14 @@ class NetworkConnectionStateReceiver @Inject constructor() : BroadcastReceiver() val mContext = context ?: return if (isInternetAvailable(mContext)) { logger.i("Connected to the internet") - EventBus.networkConnectionState.post(NetworkConnectionState.CONNECTED) + networkConnectionStateHandler.updateState(NetworkConnectionState.CONNECTED) } else { logger.i("Disconnected from the internet") - EventBus.networkConnectionState.post(NetworkConnectionState.DISCONNECTED) + networkConnectionStateHandler.updateState(NetworkConnectionState.DISCONNECTED) } } - private fun isInternetAvailable(context: Context): Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - isConnectedNewApi(context) - } else { - isConnectedOld(context) - } - - @Suppress("DEPRECATION") - fun isConnectedOld(context: Context): Boolean { - val connManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager - val networkInfo = connManager.activeNetworkInfo - return networkInfo?.isConnected == true - - } - - @RequiresApi(Build.VERSION_CODES.M) - fun isConnectedNewApi(context: Context): Boolean { + private fun isInternetAvailable(context: Context): Boolean { val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) return capabilities?.hasCapability(NET_CAPABILITY_INTERNET) == true diff --git a/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt index 36c774922..452e411dd 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt @@ -6,6 +6,7 @@ import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.combineToPair import com.tari.android.wallet.network.NetworkConnectionState +import com.tari.android.wallet.network.NetworkConnectionStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.tor.TorProxyState @@ -29,6 +30,9 @@ class ConnectionIndicatorViewModel : CommonViewModel() { @Inject lateinit var baseNodesManager: BaseNodesManager + @Inject + lateinit var networkConnectionStateHandler: NetworkConnectionStateHandler + private val _state = MutableStateFlow(UiState()) val state = _state.asStateFlow() @@ -61,7 +65,7 @@ class ConnectionIndicatorViewModel : CommonViewModel() { } private fun subscribeOnEventBus() { - EventBus.networkConnectionState.subscribe(this) { networkState -> + collectFlow(networkConnectionStateHandler.networkConnectionState) { networkState -> _state.update { it.copy(networkState = networkState) } showStatesDialog(true) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 6615d8c4e..5a89ea448 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -17,7 +17,7 @@ import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.TxId -import com.tari.android.wallet.network.NetworkConnectionState +import com.tari.android.wallet.network.NetworkConnectionStateHandler import com.tari.android.wallet.ui.common.CommonActivity import com.tari.android.wallet.ui.common.CommonFragment import com.tari.android.wallet.ui.dialog.modular.DialogArgs @@ -101,10 +101,11 @@ import javax.inject.Singleton // TODO: move navigation logic to only the navigate() method and make all navigation methods private @Singleton class TariNavigator @Inject constructor( - val prefs: CorePrefRepository, - val tariSettingsSharedRepository: TariSettingsPrefRepository, - val walletManager: WalletManager, + private val prefs: CorePrefRepository, + private val tariSettingsSharedRepository: TariSettingsPrefRepository, + private val walletManager: WalletManager, private val yatAdapter: YatAdapter, + private val networkConnection: NetworkConnectionStateHandler, ) { lateinit var activity: CommonActivity<*, *> @@ -311,7 +312,7 @@ class TariNavigator @Inject constructor( } private fun continueToAddNote(transactionData: TransactionData) { - if (EventBus.networkConnectionState.publishSubject.value != NetworkConnectionState.CONNECTED) { + if (!networkConnection.isNetworkConnected()) { showInternetConnectionErrorDialog(this.activity) return } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteFragment.kt index cad9ac5d6..96eba6aec 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteFragment.kt @@ -63,12 +63,10 @@ import com.tari.android.wallet.R.dimen.add_note_gif_inner_margin import com.tari.android.wallet.R.dimen.add_note_slide_button_left_margin import com.tari.android.wallet.R.dimen.add_note_slide_button_width import com.tari.android.wallet.databinding.FragmentAddNoteBinding -import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.observe import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.TxNote -import com.tari.android.wallet.network.NetworkConnectionState import com.tari.android.wallet.ui.common.CommonFragment import com.tari.android.wallet.ui.common.domain.PaletteManager import com.tari.android.wallet.ui.common.gyphy.repository.GifItem @@ -407,7 +405,7 @@ class AddNoteFragment : CommonFragment private fun onSlideAnimationEnd() { hideKeyboard() - if (EventBus.networkConnectionState.publishSubject.value != NetworkConnectionState.CONNECTED) { + if (!viewModel.isNetworkConnectionAvailable()) { ui.rootView.postDelayed(Constants.UI.keyboardHideWaitMs) { restoreSlider() ui.noteEditText.isEnabled = true diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteViewModel.kt index e26cc94ef..7efe07d8b 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addNote/AddNoteViewModel.kt @@ -1,12 +1,17 @@ package com.tari.android.wallet.ui.fragment.send.addNote import com.tari.android.wallet.model.TariWalletAddress +import com.tari.android.wallet.network.NetworkConnectionStateHandler import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.send.common.TransactionData +import javax.inject.Inject class AddNoteViewModel : CommonViewModel() { + @Inject + lateinit var networkConnection: NetworkConnectionStateHandler + init { component.inject(this) } @@ -18,4 +23,6 @@ class AddNoteViewModel : CommonViewModel() { fun continueToFinalizeSendTx(newData: TransactionData) { tariNavigator.navigate(Navigation.AddAmountNavigation.ContinueToFinalizing(newData)) } + + fun isNetworkConnectionAvailable(): Boolean = networkConnection.isNetworkConnected() } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt index d76d34c33..db1cebeaa 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/finalize/FinalizeSendTxViewModel.kt @@ -17,7 +17,7 @@ import com.tari.android.wallet.model.TariContact import com.tari.android.wallet.model.TransactionSendStatus import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.WalletError -import com.tari.android.wallet.network.NetworkConnectionState +import com.tari.android.wallet.network.NetworkConnectionStateHandler import com.tari.android.wallet.tor.TorBootstrapStatus import com.tari.android.wallet.tor.TorProxyState import com.tari.android.wallet.tor.TorProxyStateHandler @@ -34,6 +34,9 @@ class FinalizeSendTxViewModel : CommonViewModel() { @Inject lateinit var torProxyStateHandler: TorProxyStateHandler + @Inject + lateinit var networkConnection: NetworkConnectionStateHandler + lateinit var transactionData: TransactionData val steps: MutableLiveData> = MutableLiveData>() @@ -119,8 +122,7 @@ class FinalizeSendTxViewModel : CommonViewModel() { val secondsElapsed = Seconds.secondsBetween(connectionCheckStartTime, DateTime.now()).seconds if (secondsElapsed >= CONNECTION_TIMEOUT_SEC) { - val networkConnectionState = EventBus.networkConnectionState.publishSubject.value - if (networkConnectionState != NetworkConnectionState.CONNECTED) { + if (!networkConnection.isNetworkConnected()) { // internet connection problem txFailureReason.value = TxFailureReason.NETWORK_CONNECTION_ERROR } else { @@ -141,10 +143,9 @@ class FinalizeSendTxViewModel : CommonViewModel() { } private fun checkConnectionStatus() { - val networkConnectionState = EventBus.networkConnectionState.publishSubject.value val torProxyState = torProxyStateHandler.torProxyState.value // check internet connection - if (networkConnectionState != NetworkConnectionState.CONNECTED) { + if (!networkConnection.isNetworkConnected()) { // either not connected or Tor proxy is not running txFailureReason.value = TxFailureReason.NETWORK_CONNECTION_ERROR isCompleted = true diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt index d9f4e93d8..cf8dedddb 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt @@ -132,7 +132,6 @@ class HomeFragment : CommonFragment( override fun onDestroyView() { EventBus.unsubscribe(this) - EventBus.networkConnectionState.unsubscribe(this) super.onDestroyView() } From 2705fb2ce7b0e1ae58d8bd653ead29cc9fa764b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 16:56:23 +0200 Subject: [PATCH 05/32] Move loading contact detail logic from FFIWallet to WalletManager --- .../walletManager/WalletManager.kt | 82 ++++++++++++++++--- .../com/tari/android/wallet/ffi/FFIWallet.kt | 39 +++------ 2 files changed, 82 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index d92131dba..ece886cca 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -65,6 +65,8 @@ import com.tari.android.wallet.model.CancelledTx import com.tari.android.wallet.model.CompletedTx import com.tari.android.wallet.model.PendingInboundTx import com.tari.android.wallet.model.PendingOutboundTx +import com.tari.android.wallet.model.TariContact +import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.TransactionSendStatus import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.TxId @@ -325,40 +327,78 @@ class WalletManager @Inject constructor( * The app will crash if we try to update the UI from the FFI thread. */ override fun onTxReceived(pendingInboundTx: PendingInboundTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxReceived(pendingInboundTx)) + _walletEvent.send( + WalletEvent.Tx.TxReceived( + tx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)), + ) + ) postTxNotification(pendingInboundTx) } override fun onTxReplyReceived(pendingOutboundTx: PendingOutboundTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxReplyReceived(pendingOutboundTx)) + _walletEvent.send( + WalletEvent.Tx.TxReplyReceived( + tx = pendingOutboundTx.copy(tariContact = getUserByWalletAddress(pendingOutboundTx.tariContact.walletAddress)), + ) + ) } override fun onTxFinalized(pendingInboundTx: PendingInboundTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxFinalized(pendingInboundTx)) + _walletEvent.send( + WalletEvent.Tx.TxFinalized( + tx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)), + ) + ) } override fun onInboundTxBroadcast(pendingInboundTx: PendingInboundTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.InboundTxBroadcast(pendingInboundTx)) + _walletEvent.send( + WalletEvent.Tx.InboundTxBroadcast( + tx = pendingInboundTx.copy(tariContact = getUserByWalletAddress(pendingInboundTx.tariContact.walletAddress)), + ) + ) } override fun onOutboundTxBroadcast(pendingOutboundTx: PendingOutboundTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.OutboundTxBroadcast(pendingOutboundTx)) + _walletEvent.send( + WalletEvent.Tx.OutboundTxBroadcast( + tx = pendingOutboundTx.copy(tariContact = getUserByWalletAddress(pendingOutboundTx.tariContact.walletAddress)), + ) + ) } override fun onTxMined(completedTx: CompletedTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxMined(completedTx)) + _walletEvent.send( + WalletEvent.Tx.TxMined( + tx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)), + ) + ) } override fun onTxMinedUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxMinedUnconfirmed(completedTx, confirmationCount)) + _walletEvent.send( + WalletEvent.Tx.TxMinedUnconfirmed( + tx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)), + confirmationCount = confirmationCount, + ) + ) } override fun onTxFauxConfirmed(completedTx: CompletedTx) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxFauxConfirmed(completedTx)) + _walletEvent.send( + WalletEvent.Tx.TxFauxConfirmed( + tx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)), + ) + ) } override fun onTxFauxUnconfirmed(completedTx: CompletedTx, confirmationCount: Int) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxFauxMinedUnconfirmed(completedTx, confirmationCount)) + _walletEvent.send( + WalletEvent.Tx.TxFauxMinedUnconfirmed( + tx = completedTx.copy(tariContact = getUserByWalletAddress(completedTx.tariContact.walletAddress)), + confirmationCount = confirmationCount, + ) + ) } override fun onDirectSendResult(txId: BigInteger, status: TransactionSendStatus) = runOnMain { @@ -370,7 +410,11 @@ class WalletManager @Inject constructor( } override fun onTxCancelled(cancelledTx: CancelledTx, rejectionReason: Int) = runOnMain { - _walletEvent.send(WalletEvent.Tx.TxCancelled(cancelledTx)) + _walletEvent.send( + WalletEvent.Tx.TxCancelled( + tx = cancelledTx.copy(tariContact = getUserByWalletAddress(cancelledTx.tariContact.walletAddress)), + ) + ) // TODO don't use android components in this class val currentActivity = app.currentActivity @@ -539,6 +583,24 @@ class WalletManager @Inject constructor( // shouldn't ever reach here - no-op } + private fun getUserByWalletAddress(address: TariWalletAddress): TariContact { + val contactsFFI = requireWalletInstance.getContacts() + for (i in 0 until contactsFFI.getLength()) { + val contactFFI = contactsFFI.getAt(i) + val walletAddressFFI = contactFFI.getWalletAddress() + val tariContact = if (TariWalletAddress(walletAddressFFI) == address) TariContact(contactFFI) else null + walletAddressFFI.destroy() + contactFFI.destroy() + if (tariContact != null) { + contactsFFI.destroy() + return tariContact + } + } + // destroy native collection + contactsFFI.destroy() + return TariContact(address) + } + enum class ConnectivityStatus(val value: Int) { CONNECTING(0), ONLINE(1), diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt index ada0ad619..3d1706731 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt @@ -41,7 +41,6 @@ import com.tari.android.wallet.model.PendingInboundTx import com.tari.android.wallet.model.PendingOutboundTx import com.tari.android.wallet.model.PublicKey import com.tari.android.wallet.model.TariCoinPreview -import com.tari.android.wallet.model.TariContact import com.tari.android.wallet.model.TariUnblindedOutput import com.tari.android.wallet.model.TariVector import com.tari.android.wallet.model.TariWalletAddress @@ -379,21 +378,21 @@ class FFIWallet( private fun onTxReceived(pendingInboundTxPtr: FFIPointer) { val tx = FFIPendingInboundTx(pendingInboundTxPtr) logger.i("Tx received ${tx.getId()}") - val pendingTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val pendingTx = PendingInboundTx(tx) listener.onTxReceived(pendingTx) } private fun onTxReplyReceived(txPointer: FFIPointer) { val tx = FFICompletedTx(txPointer) logger.i("Tx reply received ${tx.getId()}") - val pendingOutboundTx = PendingOutboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val pendingOutboundTx = PendingOutboundTx(tx) listener.onTxReplyReceived(pendingOutboundTx) } private fun onTxFinalized(completedTx: FFIPointer) { val tx = FFICompletedTx(completedTx) logger.i("Tx finalized ${tx.getId()}") - val pendingInboundTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val pendingInboundTx = PendingInboundTx(tx) listener.onTxFinalized(pendingInboundTx) } @@ -402,39 +401,39 @@ class FFIWallet( logger.i("Tx broadcast ${tx.getId()}") when (tx.getDirection()) { Tx.Direction.INBOUND -> { - val pendingInboundTx = PendingInboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val pendingInboundTx = PendingInboundTx(tx) listener.onInboundTxBroadcast(pendingInboundTx) } Tx.Direction.OUTBOUND -> { - val pendingOutboundTx = PendingOutboundTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val pendingOutboundTx = PendingOutboundTx(tx) listener.onOutboundTxBroadcast(pendingOutboundTx) } } } private fun onTxMined(completedTxPtr: FFIPointer) { - val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val completed = CompletedTx(completedTxPtr) logger.i("Tx mined & confirmed ${completed.id}") listener.onTxMined(completed) } private fun onTxMinedUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() - val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val completed = CompletedTx(completedTxPtr) logger.i("Tx mined & unconfirmed ${completed.id} $confirmationCount") listener.onTxMinedUnconfirmed(completed, confirmationCount) } private fun onTxFauxConfirmed(completedTxPtr: FFIPointer) { - val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val completed = CompletedTx(completedTxPtr) logger.i("Tx faux confirmed ${completed.id}") listener.onTxMined(completed) } private fun onTxFauxUnconfirmed(completedTxPtr: FFIPointer, confirmationCountBytes: ByteArray) { val confirmationCount = BigInteger(1, confirmationCountBytes).toInt() - val completed = CompletedTx(completedTxPtr).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val completed = CompletedTx(completedTxPtr) logger.i("Tx faux unconfirmed ${completed.id}") listener.onTxMinedUnconfirmed(completed, confirmationCount) } @@ -449,7 +448,7 @@ class FFIWallet( val rejectionReasonInt = BigInteger(1, rejectionReason).toInt() val tx = FFICompletedTx(completedTx) logger.i("Tx cancelled ${tx.getId()}") - val cancelledTx = CancelledTx(tx).let { it.copy(tariContact = getUserByWalletAddress(it.tariContact.walletAddress)) } + val cancelledTx = CancelledTx(tx) if (tx.getDirection() == Tx.Direction.OUTBOUND) { listener.onTxCancelled(cancelledTx, rejectionReasonInt) } @@ -498,22 +497,4 @@ class FFIWallet( private fun onContactLivenessDataUpdated(livenessUpdate: FFIPointer) { logger.i("OnContactLivenessDataUpdated") } - - private fun getUserByWalletAddress(address: TariWalletAddress): TariContact { - val contactsFFI = getContacts() - for (i in 0 until contactsFFI.getLength()) { - val contactFFI = contactsFFI.getAt(i) - val walletAddressFFI = contactFFI.getWalletAddress() - val tariContact = if (TariWalletAddress(walletAddressFFI) == address) TariContact(contactFFI) else null - walletAddressFFI.destroy() - contactFFI.destroy() - if (tariContact != null) { - contactsFFI.destroy() - return tariContact - } - } - // destroy native collection - contactsFFI.destroy() - return TariContact(address) - } } From 43081fe729ae3318b007dfc66fd63cfc83e57fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 17:13:34 +0200 Subject: [PATCH 06/32] Create BaseNodeStateHandler for handling BaseNodeState and BaseNodeSyncState --- .../application/baseNodes/BaseNodesManager.kt | 5 -- .../walletManager/WalletManager.kt | 65 ++++++++++--------- .../baseNode/BaseNodePrefRepository.kt | 21 ------ .../com/tari/android/wallet/event/EventBus.kt | 14 ---- .../service/baseNode/BaseNodeStateHandler.kt | 31 +++++++++ .../ConnectionIndicatorViewModel.kt | 10 +-- .../changeBaseNode/ChangeBaseNodeViewModel.kt | 17 +++-- .../themeSelector/ThemeSelectorViewModel.kt | 9 ++- 8 files changed, 87 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/com/tari/android/wallet/service/baseNode/BaseNodeStateHandler.kt diff --git a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt index a67df8654..63dcd3003 100644 --- a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt @@ -10,7 +10,6 @@ import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.ffi.FFITariBaseNodeState import com.tari.android.wallet.ffi.FFIWallet -import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.util.DebugConfig import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -113,10 +112,6 @@ class BaseNodesManager @Inject constructor( baseNodeSharedRepository.ffiBaseNodes = loadBaseNodesFromFFI(wallet) } - fun setBaseNodeState(state: BaseNodeState) { - baseNodeSharedRepository.baseNodeState = state - } - private fun loadBaseNodesFromFFI(wallet: FFIWallet): BaseNodeList = wallet.getBaseNodePeers() .mapIndexed { index, publicKey -> BaseNodeDto( diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index ece886cca..d0b748927 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -74,6 +74,7 @@ import com.tari.android.wallet.model.fullBase58 import com.tari.android.wallet.model.recovery.WalletRestorationResult import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.service.baseNode.BaseNodeState +import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.service.notification.NotificationService import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository @@ -122,6 +123,7 @@ class WalletManager @Inject constructor( private val baseNodesManager: BaseNodesManager, private val torConfig: TorConfig, private val torProxyStateHandler: TorProxyStateHandler, + private val baseNodeStateHandler: BaseNodeStateHandler, private val app: TariWalletApplication, private val notificationHelper: NotificationHelper, private val notificationService: NotificationService, @@ -132,6 +134,8 @@ class WalletManager @Inject constructor( var walletInstance: FFIWallet? get() = atomicInstance.get() set(value) = atomicInstance.set(value) + val requireWalletInstance: FFIWallet + get() = walletInstance ?: error("Wallet instance is null") private val _walletState = MutableStateFlow(WalletState.NotReady) val walletState = _walletState.asStateFlow() @@ -205,38 +209,37 @@ class WalletManager @Inject constructor( doOnWalletRunning { wallet -> while (currentBaseNode != null) { try { - currentBaseNode?.let { - logger.i( - "startSync:publicKeyHex: ${it.publicKeyHex}\n" + - "startSync:address: ${it.address}\n" + - "startSync:userBaseNodes: ${Gson().toJson(baseNodesManager.userBaseNodes)}" - ) - + currentBaseNode?.let { it -> + logger.i("baseNodeSync: sync with base node ${it.publicKeyHex}::${it.address} started") val baseNodeKeyFFI = FFIPublicKey(HexString(it.publicKeyHex)) val addBaseNodeResult = wallet.addBaseNodePeer(baseNodeKeyFFI, it.address) baseNodeKeyFFI.destroy() - logger.i("startSync:addBaseNodePeer ${if (addBaseNodeResult) "success" else "failed"}") + logger.i("baseNodeSync:addBaseNodePeer ${if (addBaseNodeResult) "success" else "failed"}") try { - logger.i("startSync:wallet validation:start Tx and TXO validation") + logger.i("baseNodeSync:wallet validation:start Tx and TXO validation") walletValidationStatusMap.clear() walletValidationStatusMap[WalletValidationType.TXO] = WalletValidationResult(wallet.startTXOValidation(), null) walletValidationStatusMap[WalletValidationType.TX] = WalletValidationResult(wallet.startTxValidation(), null) + logger.i( + "baseNodeSync:wallet validation:started Tx and TXO validation with " + + "request keys: ${Gson().toJson(walletValidationStatusMap.map { it.value.requestKey })}" + ) } catch (e: Throwable) { - logger.i("startSync:wallet validation:error: ${e.message}") + logger.i("baseNodeSync:wallet validation:error: ${e.message}") walletValidationStatusMap.clear() - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) + baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Failed) } } break } catch (e: Throwable) { - logger.i("startSync:error connecting to base node $currentBaseNode with an error: ${e.message}") + logger.i("baseNodeSync:error connecting to base node $currentBaseNode with an error: ${e.message}") currentBaseNode = baseNodesManager.setNextBaseNode() } } if (currentBaseNode == null) { - logger.e("startSync: cannot connect to any base node") + logger.e("baseNodeSync: cannot connect to any base node") } } } @@ -439,12 +442,14 @@ class WalletManager @Inject constructor( responseId = responseId, isSuccess = status == TransactionValidationStatus.Success, ) - walletInstance - ?.takeIf { !txBroadcastRestarted && status == TransactionValidationStatus.Success } - ?.let { wallet -> - wallet.restartTxBroadcast() + walletInstance?.let { + if (!txBroadcastRestarted && status == TransactionValidationStatus.Success) { + it.restartTxBroadcast() txBroadcastRestarted = true - } ?: logger.i("Transaction broadcast restart failed because wallet instance is null or tx broadcast already restarted") + logger.i("baseNodeSync:wallet validation: Transaction broadcast restarted (requestId: $responseId)") + } + } + ?: logger.i("baseNodeSync:wallet validation:error: Transaction broadcast restart failed because wallet instance is null (requestId: $responseId)\"") } override fun onBalanceUpdated(balanceInfo: BalanceInfo) = runOnMain { @@ -454,14 +459,14 @@ class WalletManager @Inject constructor( override fun onConnectivityStatus(status: Int) = runOnMain { when (ConnectivityStatus.entries[status]) { ConnectivityStatus.CONNECTING -> { - /* do nothing */ + baseNodeStateHandler.updateState(BaseNodeState.Syncing) + logger.i("baseNodeSync:base nodes state: connecting to ${baseNodesManager.currentBaseNode?.publicKeyHex}") } ConnectivityStatus.ONLINE -> { - walletInstance?.let { baseNodesManager.refreshBaseNodeList(it) } - ?: logger.i("Wallet instance is null when trying to refresh base node list") - baseNodesManager.setBaseNodeState(BaseNodeState.Online) - EventBus.baseNodeState.post(BaseNodeState.Online) + baseNodesManager.refreshBaseNodeList(requireWalletInstance) + baseNodeStateHandler.updateState(BaseNodeState.Online) + logger.i("baseNodeSync:base nodes state: connected to ${baseNodesManager.currentBaseNode?.publicKeyHex} ONLINE") } ConnectivityStatus.OFFLINE -> { @@ -470,8 +475,8 @@ class WalletManager @Inject constructor( baseNodesManager.setNextBaseNode() syncBaseNode() } - baseNodesManager.setBaseNodeState(BaseNodeState.Offline) - EventBus.baseNodeState.post(BaseNodeState.Offline) + baseNodeStateHandler.updateState(BaseNodeState.Offline) + logger.i("baseNodeSync:base nodes state: disconnected from ${baseNodesManager.currentBaseNode?.publicKeyHex} OFFLINE") } } } @@ -509,7 +514,6 @@ class WalletManager @Inject constructor( if (baseNodesManager.currentBaseNode == null) { baseNodesManager.setNextBaseNode() } - syncBaseNode() saveWalletAddressToSharedPrefs() } } @@ -546,10 +550,10 @@ class WalletManager @Inject constructor( val currentStatus = walletValidationStatusMap[type] ?: return if (currentStatus.requestKey != responseId) return walletValidationStatusMap[type] = WalletValidationResult(currentStatus.requestKey, isSuccess) - logger.i("startSync:wallet validation:validation result: $type: $isSuccess") + logger.i("baseNodeSync:wallet validation:validation result for request $responseId: $type: $isSuccess") checkBaseNodeSyncCompletion() } catch (e: Throwable) { - logger.i("startSync:wallet validation $type:error: ${e.message}") + logger.i("baseNodeSync:wallet validation $type for request $responseId failed with an error: ${e.message}") } } @@ -566,19 +570,20 @@ class WalletManager @Inject constructor( baseNodesManager.setNextBaseNode() syncBaseNode() } - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Failed) // TODO replace with flow!!! + baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Failed) return } // if any of the results is null, we're still waiting for all callbacks to happen val inProgress = statusMapCopy.any { it.value.isSuccess == null } if (inProgress) { + baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Syncing) return } // check if it's successful val successful = statusMapCopy.all { it.value.isSuccess == true } if (successful) { walletValidationStatusMap.clear() - EventBus.baseNodeSyncState.post(BaseNodeSyncState.Online) // TODO replace with flow!!! + baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Online) } // shouldn't ever reach here - no-op } diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt index ccf0dc542..d64b13052 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/baseNode/BaseNodePrefRepository.kt @@ -36,11 +36,8 @@ import android.content.SharedPreferences import com.tari.android.wallet.data.sharedPrefs.CommonPrefRepository import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonNullableDelegate -import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefIntDelegate import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.network.formatKey -import com.tari.android.wallet.event.EventBus -import com.tari.android.wallet.service.baseNode.BaseNodeState import javax.inject.Inject import javax.inject.Singleton @@ -53,7 +50,6 @@ class BaseNodePrefRepository @Inject constructor( private object Key { const val CURRENT_BASE_NODE = "tari_wallet_current_base_node" const val USER_BASE_NODE_LIST = "tari_wallet_user_base_nodes" - const val BASE_NODE_STATE = "tari_wallet_user_base_node_state" const val FFI_BASE_NODE_LIST = "FFI_BASE_NODE_LIST" } @@ -79,23 +75,6 @@ class BaseNodePrefRepository @Inject constructor( defValue = BaseNodeList(), ) - // ordinal value of BaseNodeState enum class - private var baseNodeStateOrdinal: Int by SharedPrefIntDelegate( - prefs = sharedPrefs, - commonRepository = this, - name = Key.BASE_NODE_STATE, - defValue = BaseNodeState.Syncing.ordinal, - ) - var baseNodeState: BaseNodeState - get() = BaseNodeState.get(baseNodeStateOrdinal) - set(value) { - baseNodeStateOrdinal = value.ordinal - } - - init { - EventBus.baseNodeState.post(baseNodeState) - } - fun clear() { currentBaseNode = null userBaseNodes = BaseNodeList() diff --git a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt index cc82067bd..dba43fe6f 100644 --- a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt +++ b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt @@ -35,8 +35,6 @@ package com.tari.android.wallet.event import com.tari.android.wallet.infrastructure.backup.BackupsState import com.tari.android.wallet.model.BalanceInfo import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.service.baseNode.BaseNodeState -import com.tari.android.wallet.service.baseNode.BaseNodeSyncState /** * Event bus for the pub/sub model. @@ -53,29 +51,17 @@ object EventBus : GeneralEventBus() { val backupState = BehaviorEventBus() - val baseNodeState = BehaviorEventBus() - - val baseNodeSyncState = BehaviorEventBus() - val walletRestorationState = BehaviorEventBus() - init { - baseNodeSyncState.post(BaseNodeSyncState.Syncing) - } - fun unsubscribeAll(subscriber: Any) { EventBus.unsubscribe(subscriber) backupState.unsubscribe(subscriber) - baseNodeState.unsubscribe(subscriber) - baseNodeSyncState.unsubscribe(subscriber) walletRestorationState.unsubscribe(subscriber) } override fun clear() { super.clear() backupState.clear() - baseNodeState.clear() - baseNodeSyncState.clear() walletRestorationState.clear() } } diff --git a/app/src/main/java/com/tari/android/wallet/service/baseNode/BaseNodeStateHandler.kt b/app/src/main/java/com/tari/android/wallet/service/baseNode/BaseNodeStateHandler.kt new file mode 100644 index 000000000..d73495e7c --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/service/baseNode/BaseNodeStateHandler.kt @@ -0,0 +1,31 @@ +package com.tari.android.wallet.service.baseNode + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BaseNodeStateHandler @Inject constructor() { + + /** + * Base node state. Showing the wallet is connected to the base node or not. + */ + private val _baseNodeState = MutableStateFlow(BaseNodeState.Syncing) + val baseNodeState = _baseNodeState.asStateFlow() + + /** + * Base node sync state. Showing the wallet validation status after connecting to the base node. + */ + private val _baseNodeSyncState = MutableStateFlow(BaseNodeSyncState.NotStarted) + val baseNodeSyncState = _baseNodeSyncState.asStateFlow() + + fun updateState(state: BaseNodeState) { + _baseNodeState.update { state } + } + + fun updateSyncState(state: BaseNodeSyncState) { + _baseNodeSyncState.update { state } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt index 452e411dd..64c05f0f1 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/component/networkStateIndicator/ConnectionIndicatorViewModel.kt @@ -2,12 +2,12 @@ package com.tari.android.wallet.ui.component.networkStateIndicator import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager -import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.combineToPair import com.tari.android.wallet.network.NetworkConnectionState import com.tari.android.wallet.network.NetworkConnectionStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeState +import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.tor.TorProxyState import com.tari.android.wallet.tor.TorProxyStateHandler @@ -33,6 +33,9 @@ class ConnectionIndicatorViewModel : CommonViewModel() { @Inject lateinit var networkConnectionStateHandler: NetworkConnectionStateHandler + @Inject + lateinit var baseNodeStateHandler: BaseNodeStateHandler + private val _state = MutableStateFlow(UiState()) val state = _state.asStateFlow() @@ -69,15 +72,14 @@ class ConnectionIndicatorViewModel : CommonViewModel() { _state.update { it.copy(networkState = networkState) } showStatesDialog(true) } - EventBus.baseNodeState.subscribe(this) { baseNodeState -> + collectFlow(baseNodeStateHandler.baseNodeState) { baseNodeState -> _state.update { it.copy(baseNodeState = baseNodeState) } showStatesDialog(true) } - EventBus.baseNodeSyncState.subscribe(this) { syncState -> + collectFlow(baseNodeStateHandler.baseNodeSyncState) { syncState -> _state.update { it.copy(baseNodeSyncState = syncState) } showStatesDialog(true) } - collectFlow(torProxyStateHandler.torProxyState) { torProxyState -> _state.update { it.copy(torProxyState = torProxyState) } showStatesDialog(true) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt index abf33b3a6..6ad5e1f06 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt @@ -6,13 +6,13 @@ import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.application.deeplinks.DeeplinkHandler import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodePrefRepository -import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.extension.collectFlow +import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.recyclerView.CommonViewHolderItem -import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.DialogArgs import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs +import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule @@ -28,10 +28,10 @@ import javax.inject.Inject class ChangeBaseNodeViewModel : CommonViewModel() { @Inject - lateinit var baseNodeSharedRepository: BaseNodePrefRepository + lateinit var baseNodesManager: BaseNodesManager @Inject - lateinit var baseNodesManager: BaseNodesManager + lateinit var baseNodeStateHandler: BaseNodeStateHandler @Inject lateinit var deeplinkHandler: DeeplinkHandler @@ -45,7 +45,7 @@ class ChangeBaseNodeViewModel : CommonViewModel() { init { component.inject(this) - EventBus.baseNodeState.subscribe(this) { loadList() } + collectFlow(baseNodeStateHandler.baseNodeState) { loadList() } loadList() } @@ -77,9 +77,9 @@ class ChangeBaseNodeViewModel : CommonViewModel() { } private fun loadList() { - val currentBaseNode = baseNodeSharedRepository.currentBaseNode + val currentBaseNode = baseNodesManager.currentBaseNode val items = mutableListOf() - items.addAll(baseNodeSharedRepository.userBaseNodes.map { BaseNodeViewHolderItem(it, currentBaseNode, this::deleteBaseNode) }) + items.addAll(baseNodesManager.userBaseNodes.map { BaseNodeViewHolderItem(it, currentBaseNode, this::deleteBaseNode) }) items.addAll(baseNodesManager.baseNodeList.map { BaseNodeViewHolderItem(it, currentBaseNode, this::deleteBaseNode) }) _baseNodeList.postValue(items) } @@ -150,5 +150,4 @@ class ChangeBaseNodeViewModel : CommonViewModel() { ) } } - } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/themeSelector/ThemeSelectorViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/themeSelector/ThemeSelectorViewModel.kt index 6c48ef770..8098d3efd 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/themeSelector/ThemeSelectorViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/themeSelector/ThemeSelectorViewModel.kt @@ -1,13 +1,18 @@ package com.tari.android.wallet.ui.fragment.settings.themeSelector import androidx.lifecycle.MutableLiveData -import com.tari.android.wallet.event.EventBus +import com.tari.android.wallet.extension.collectFlow +import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.fragment.settings.themeSelector.adapter.ThemeViewHolderItem +import javax.inject.Inject class ThemeSelectorViewModel : CommonViewModel() { + @Inject + lateinit var baseNodeStateHandler: BaseNodeStateHandler + val themes: MutableLiveData> = MutableLiveData() val newTheme = SingleLiveEvent() @@ -15,7 +20,7 @@ class ThemeSelectorViewModel : CommonViewModel() { init { component.inject(this) - EventBus.baseNodeState.subscribe(this) { loadList() } + collectFlow(baseNodeStateHandler.baseNodeState) { loadList() } loadList() } From b9a518d32c2fe828ec7542f9e18205d04c1180dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 3 Oct 2024 17:15:41 +0200 Subject: [PATCH 07/32] Increase version to 0.27.1(307) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 76b914395..a794b1acd 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.coroutines_version = '1.8.0' // build & version - ext.buildNumber = 306 + ext.buildNumber = 307 ext.versionNumber = "0.27.1" // JNI libs From 828e3e67c1c63c9548b8689c30ec497e1cf9af56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Fri, 4 Oct 2024 19:02:10 +0200 Subject: [PATCH 08/32] Refactor Qr scanner, add the PaperWallet deeplink --- .../wallet/application/DeepLinkTest.kt | 4 +- app/src/main/AndroidManifest.xml | 2 +- .../wallet/application/deeplinks/DeepLink.kt | 38 +++- .../deeplinks/DeeplinkFormatter.kt | 80 -------- .../application/deeplinks/DeeplinkHandler.kt | 78 ++++++- .../deeplinks/DeeplinkViewModel.kt | 28 ++- .../sharedPrefs/tor/TorBridgeConfiguration.kt | 6 +- .../android/wallet/di/ApplicationComponent.kt | 8 +- .../bluetooth/TariBluetoothClient.kt | 2 +- .../bluetooth/TariBluetoothServer.kt | 4 +- .../wallet/service/service/WalletService.kt | 12 +- .../service/service/WalletServiceLauncher.kt | 12 +- .../WalletAddressViewModel.kt | 2 +- .../add/SelectUserContactFragment.kt | 4 +- .../ContactSelectionFragment.kt | 16 +- .../ContactSelectionViewModel.kt | 9 +- .../contactBook/root/ContactBookFragment.kt | 12 +- .../contactBook/root/ContactBookViewModel.kt | 10 +- .../contactBook/root/ShareViewModel.kt | 2 +- .../wallet/ui/fragment/home/HomeActivity.kt | 4 +- .../fragment/profile/WalletInfoViewModel.kt | 2 +- .../ui/fragment/qr/QRScannerViewModel.kt | 161 --------------- ...cannerActivity.kt => QrScannerActivity.kt} | 45 ++-- .../wallet/ui/fragment/qr/QrScannerModel.kt | 16 ++ .../wallet/ui/fragment/qr/QrScannerSource.kt | 1 + .../ui/fragment/qr/QrScannerViewModel.kt | 175 ++++++++++++++++ .../restore/activity/WalletRestoreActivity.kt | 10 - .../send/requestTari/RequestTariViewModel.kt | 2 +- .../changeBaseNode/ChangeBaseNodeViewModel.kt | 2 +- .../TorBridgesSelectionViewModel.kt | 2 +- .../customBridges/CustomTorBridgesFragment.kt | 12 +- .../CustomTorBridgesViewModel.kt | 5 +- .../wallet/ui/fragment/tx/HomeFragment.kt | 12 +- .../main/res/layout/activity_qr_scanner.xml | 193 +++++++++--------- app/src/main/res/values/strings.xml | 1 + 35 files changed, 512 insertions(+), 460 deletions(-) delete mode 100644 app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkFormatter.kt delete mode 100644 app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerViewModel.kt rename app/src/main/java/com/tari/android/wallet/ui/fragment/qr/{QRScannerActivity.kt => QrScannerActivity.kt} (82%) create mode 100644 app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerModel.kt create mode 100644 app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerViewModel.kt diff --git a/app/src/androidTest/java/com/tari/android/wallet/application/DeepLinkTest.kt b/app/src/androidTest/java/com/tari/android/wallet/application/DeepLinkTest.kt index 945fd389d..1199bd18f 100644 --- a/app/src/androidTest/java/com/tari/android/wallet/application/DeepLinkTest.kt +++ b/app/src/androidTest/java/com/tari/android/wallet/application/DeepLinkTest.kt @@ -50,14 +50,14 @@ class DeepLinkTest { fun assertBaseNodeName() { val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.COMMAND_ADD_NODE}?${DeepLink.AddBaseNode.KEY_NAME}=base_node_test" - val result = deeplinkHandler.handle(deeplink) as? DeepLink.AddBaseNode + val result = deeplinkHandler.parseDeepLink(deeplink) as? DeepLink.AddBaseNode assertEquals(result!!.name, "base_node_test") } @Test fun assertBaseNodePeer() { val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.COMMAND_ADD_NODE}?${DeepLink.AddBaseNode.KEY_PEER}=$PEER" - val result = deeplinkHandler.handle(deeplink) as? DeepLink.AddBaseNode + val result = deeplinkHandler.parseDeepLink(deeplink) as? DeepLink.AddBaseNode assertEquals(result!!.peer, PEER) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ec47ca519..4c7d35541 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -192,7 +192,7 @@ = emptyMap() open fun getCommand(): String = "" // tari://esmeralda/contacts?list[0][alias]=Name&list[0][tariAddress]=tariAddress&list[1][alias]=Name&list[1][tariAddress]=tariAddress + @Parcelize data class Contacts(val contacts: List) : DeepLink() { constructor(params: Map) : this( @@ -79,7 +82,8 @@ sealed class DeepLink { const val KEY_TARI_ADDRESS = "tariAddress" } - data class DeeplinkContact(val alias: String, val tariAddress: Base58) + @Parcelize + data class DeeplinkContact(val alias: String, val tariAddress: Base58) : Parcelable class FormatExtractor(val key: String, val value: String = "") { val index: Int @@ -97,6 +101,7 @@ sealed class DeepLink { } } + @Parcelize data class Send(val walletAddress: Base58 = "", val amount: MicroTari? = null, val note: String = "") : DeepLink() { constructor(params: Map) : this( @@ -121,6 +126,7 @@ sealed class DeepLink { } } + @Parcelize data class UserProfile(val tariAddress: Base58 = "", val alias: String = "") : DeepLink() { constructor(params: Map) : this( @@ -142,6 +148,7 @@ sealed class DeepLink { } } + @Parcelize data class AddBaseNode(val name: String = "", val peer: String = "") : DeepLink() { constructor(params: Map) : this( @@ -163,8 +170,34 @@ sealed class DeepLink { } } + @Parcelize data class TorBridges(val torConfigurations: List) : DeepLink() + // tari://esmeralda/paper_wallet?seed_words[0]=young&seed_words[1]=shrimp&seed_words[2]=day&seed_words[3]=mountain&seed_words[4]=mammal&seed_words[5]=pond&seed_words[6]=shrimp&seed_words[7]=mammal&seed_words[8]=loyal&seed_words[9]=young&seed_words[10]=whisper&seed_words[11]=glare&seed_words[12]=stuff&seed_words[13]=around&seed_words[14]=estate&seed_words[15]=fabric&seed_words[16]=faint&seed_words[17]=goddess&seed_words[18]=crew&seed_words[19]=custom&seed_words[20]=disagree&seed_words[21]=fox&seed_words[22]=fragile&seed_words[23]=impact + @Parcelize + data class PaperWallet(val seedWords: List) : DeepLink() { + constructor(params: Map) : this( + params.filterKeys { it.startsWith("seed_words[") } + .toSortedMap(compareBy { it.substringAfter("[").substringBefore("]").toInt() }) + .values + .toList() + ) + + override fun getParams(): Map { + val params = hashMapOf() + seedWords.forEachIndexed { index, seedWord -> + params["seed_words[$index]"] = seedWord + } + return params + } + + override fun getCommand(): String = COMMAND_PAPER_WALLET + + companion object { + const val COMMAND_PAPER_WALLET = "paper_wallet" + } + } + companion object { fun getByCommand(command: String, params: Map): DeepLink? = when (command) { @@ -172,6 +205,7 @@ sealed class DeepLink { Send.COMMAND_SEND -> Send(params) AddBaseNode.COMMAND_ADD_NODE -> AddBaseNode(params) UserProfile.COMMAND_PROFILE -> UserProfile(params) + PaperWallet.COMMAND_PAPER_WALLET -> PaperWallet(params) else -> null } } diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkFormatter.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkFormatter.kt deleted file mode 100644 index eb7450006..000000000 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkFormatter.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.tari.android.wallet.application.deeplinks - -import android.net.Uri -import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository -import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfiguration -import com.tari.android.wallet.model.TariWalletAddress -import java.net.URLDecoder -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class DeeplinkFormatter @Inject constructor(private val networkRepository: NetworkPrefRepository) { - fun parse(deepLink: String): DeepLink? { - val torBridges = getTorDeeplink(deepLink) - if (torBridges.isNotEmpty()) { - return DeepLink.TorBridges(torBridges) - } - - val uri = runCatching { Uri.parse(URLDecoder.decode(deepLink, "UTF-8")) }.getOrNull() ?: return null - - if (!uri.authority.equals(networkRepository.currentNetwork.network.uriComponent)) { - return null - } - - var paramentrs = uri.queryParameterNames.associateWith { uri.getQueryParameter(it).orEmpty() }.toMutableMap() - val command = uri.path.orEmpty().trimStart('/') - if (command == DeepLink.Contacts.COMMAND_CONTACTS) { - val values = uri.query.orEmpty().split("&").map { - val (key, value) = it.split("=") - key to value - }.toMap() - paramentrs = values.toMutableMap() - } - - return DeepLink.getByCommand(command, paramentrs)?.takeIf { - when (it) { - is DeepLink.Send -> TariWalletAddress.validateBase58(it.walletAddress) - is DeepLink.UserProfile -> TariWalletAddress.validateBase58(it.tariAddress) - else -> true // Handle other DeepLink types or consider returning null if they shouldn't be valid - } - } - } - - fun toDeeplink(deepLink: DeepLink): String { - if (deepLink is DeepLink.TorBridges) { - return deepLink.torConfigurations.joinToString("\n") { - "${it.ip}:${it.port} ${it.fingerprint}" - } - } - - val fullPart = Uri.Builder() - .scheme(scheme) - .authority(networkRepository.currentNetwork.network.uriComponent) - .appendPath(deepLink.getCommand()) - - deepLink.getParams().forEach { (key, value) -> - fullPart.appendQueryParameter(key, value) - } - - return fullPart.build().toString() - } - - private fun getTorDeeplink(input: String): List { - return regex.findAll(input).mapNotNull { match -> - try { - val ipAddressAndPort = match.groupValues[1].split(":") - val sha1Hash = match.groupValues[2] - TorBridgeConfiguration("", ipAddressAndPort[0], ipAddressAndPort[1], sha1Hash) - } catch (e: Exception) { - null - } - }.toList() - } - - companion object { - const val scheme = "tari" - - val regex = Regex("""(\d+\.\d+\.\d+\.\d+:\d+) ([0-9A-Fa-f]+)""") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt index 52cdc1f0f..70ad88833 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt @@ -1,16 +1,84 @@ package com.tari.android.wallet.application.deeplinks +import android.net.Uri import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository +import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfiguration +import com.tari.android.wallet.model.TariWalletAddress +import java.net.URLDecoder import javax.inject.Inject import javax.inject.Singleton - @Singleton -class DeeplinkHandler @Inject constructor(networkRepository: NetworkPrefRepository) { +class DeeplinkHandler @Inject constructor(private val networkRepository: NetworkPrefRepository) { + + fun parseDeepLink(deepLink: String): DeepLink? = parse(deepLink) + + fun getDeeplinkString(deeplink: DeepLink): String = toDeeplink(deeplink) + + private fun parse(deepLink: String): DeepLink? { + val torBridges = getTorDeeplink(deepLink) + if (torBridges.isNotEmpty()) { + return DeepLink.TorBridges(torBridges) + } + + val uri = runCatching { Uri.parse(URLDecoder.decode(deepLink, "UTF-8")) }.getOrNull() ?: return null + + if (!uri.authority.equals(networkRepository.currentNetwork.network.uriComponent)) { + return null + } + + val command = uri.path.orEmpty().trimStart('/') + val parameters = if (command == DeepLink.Contacts.COMMAND_CONTACTS || command == DeepLink.PaperWallet.COMMAND_PAPER_WALLET) { // list params + uri.query.orEmpty().split("&").associate { + val (key, value) = it.split("=") + key to value + } + } else { + uri.queryParameterNames.associateWith { uri.getQueryParameter(it).orEmpty() } + } + + return DeepLink.getByCommand(command, parameters)?.takeIf { + when (it) { + is DeepLink.Send -> TariWalletAddress.validateBase58(it.walletAddress) + is DeepLink.UserProfile -> TariWalletAddress.validateBase58(it.tariAddress) + else -> true // Handle other DeepLink types or consider returning null if they shouldn't be valid + } + } + } + + private fun toDeeplink(deepLink: DeepLink): String { + if (deepLink is DeepLink.TorBridges) { + return deepLink.torConfigurations.joinToString("\n") { + "${it.ip}:${it.port} ${it.fingerprint}" + } + } + + val fullPart = Uri.Builder() + .scheme(SCHEME) + .authority(networkRepository.currentNetwork.network.uriComponent) + .appendPath(deepLink.getCommand()) + + deepLink.getParams().forEach { (key, value) -> + fullPart.appendQueryParameter(key, value) + } - private val deeplinkFormatter = DeeplinkFormatter(networkRepository) + return fullPart.build().toString() + } - fun handle(deepLink: String): DeepLink? = deeplinkFormatter.parse(deepLink) + private fun getTorDeeplink(input: String): List { + return REGEX.findAll(input).mapNotNull { match -> + try { + val ipAddressAndPort = match.groupValues[1].split(":") + val sha1Hash = match.groupValues[2] + TorBridgeConfiguration("", ipAddressAndPort[0], ipAddressAndPort[1], sha1Hash) + } catch (e: Exception) { + null + } + }.toList() + } - fun getDeeplink(deeplink: DeepLink): String = deeplinkFormatter.toDeeplink(deeplink) + companion object { + const val SCHEME = "tari" + val REGEX = Regex("""(\d+\.\d+\.\d+\.\d+:\d+) ([0-9A-Fa-f]+)""") + } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt index 2e0ef35fd..569317a20 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt @@ -39,7 +39,7 @@ class DeeplinkViewModel : CommonViewModel() { } fun tryToHandle(qrData: String, isQrData: Boolean = true) { - deeplinkHandler.handle(qrData)?.let { execute(it, isQrData) } + deeplinkHandler.parseDeepLink(qrData)?.let { execute(it, isQrData) } } fun execute(deeplink: DeepLink, isQrData: Boolean = true) { @@ -49,6 +49,18 @@ class DeeplinkViewModel : CommonViewModel() { is DeepLink.Send -> sendAction(deeplink, isQrData) is DeepLink.UserProfile -> addUserProfile(deeplink, isQrData) is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) + } + } + + fun executeRawDeeplink(deeplink: DeepLink, isQrData: Boolean = true) { + when (deeplink) { + is DeepLink.AddBaseNode -> addBaseNode(deeplink) + is DeepLink.Contacts -> addContactsAction(getData(deeplink), isQrData) + is DeepLink.Send -> sendAction(deeplink, isQrData) + is DeepLink.UserProfile -> addContactsAction(getData(deeplink)?.let { listOf(it) } ?: listOf(), isQrData) + is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) } } @@ -79,7 +91,7 @@ class DeeplinkViewModel : CommonViewModel() { addContacts(contact, isQrData) } - fun addContacts(contacts: DeepLink.Contacts, isQrData: Boolean = true) { + private fun addContacts(contacts: DeepLink.Contacts, isQrData: Boolean = true) { val contactDtos = getData(contacts) if (contactDtos.isEmpty()) return val names = contactDtos.joinToString(", ") { it.contactInfo.getAlias().trim() } @@ -94,20 +106,14 @@ class DeeplinkViewModel : CommonViewModel() { ) } - fun addTorBridges(deeplink: DeepLink.TorBridges, isQrData: Boolean) { + private fun addTorBridges(deeplink: DeepLink.TorBridges, isQrData: Boolean) { deeplink.torConfigurations.forEach { torSharedRepository.addTorBridgeConfiguration(it) } } - fun executeRawDeeplink(deeplink: DeepLink, isQrData: Boolean = true) { - when (deeplink) { - is DeepLink.AddBaseNode -> addBaseNode(deeplink) - is DeepLink.Contacts -> addContactsAction(getData(deeplink), isQrData) - is DeepLink.Send -> sendAction(deeplink, isQrData) - is DeepLink.UserProfile -> addContactsAction(getData(deeplink)?.let { listOf(it) } ?: listOf(), isQrData) - is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - } + private fun showPaperWalletDialog(deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { + showNotReadyYetDialog() } private fun getData(deeplink: DeepLink.AddBaseNode): BaseNodeDto = BaseNodeDto.fromDeeplink(deeplink) diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/tor/TorBridgeConfiguration.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/tor/TorBridgeConfiguration.kt index 914201a40..a3592f4a6 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/tor/TorBridgeConfiguration.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/tor/TorBridgeConfiguration.kt @@ -1,5 +1,9 @@ package com.tari.android.wallet.data.sharedPrefs.tor +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class TorBridgeConfiguration( val transportTechnology: String, val ip: String, @@ -7,7 +11,7 @@ data class TorBridgeConfiguration( val fingerprint: String, val certificate: String = "", val iatMode: String = "" -) { +) : Parcelable { override fun toString(): String = ("$transportTechnology $ip:$port $fingerprint ${if (certificate.isNotBlank()) "cert=$certificate" else ""} " + if (iatMode.isNotBlank()) "iat-mode=$iatMode" else "").trim() } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt index 84710896d..c5d3db3b5 100644 --- a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt +++ b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt @@ -60,8 +60,8 @@ import com.tari.android.wallet.ui.fragment.onboarding.inroduction.IntroductionVi import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthViewModel import com.tari.android.wallet.ui.fragment.pinCode.EnterPinCodeViewModel import com.tari.android.wallet.ui.fragment.profile.WalletInfoViewModel -import com.tari.android.wallet.ui.fragment.qr.QRScannerActivity -import com.tari.android.wallet.ui.fragment.qr.QRScannerViewModel +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerViewModel import com.tari.android.wallet.ui.fragment.restore.activity.WalletRestoreActivity import com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.ChooseRestoreOptionViewModel import com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword.EnterRestorationPasswordViewModel @@ -134,7 +134,7 @@ interface ApplicationComponent { fun inject(activity: OnboardingFlowActivity) fun inject(activity: AuthActivity) fun inject(activity: HomeActivity) - fun inject(activity: QRScannerActivity) + fun inject(activity: QrScannerActivity) fun inject(activity: WalletRestoreActivity) fun inject(fragment: ChooseGIFDialogFragment) @@ -196,7 +196,7 @@ interface ApplicationComponent { fun inject(viewModel: TransactionHistoryViewModel) fun inject(viewModel: BluetoothSettingsViewModel) fun inject(viewModel: WalletAddressViewModel) - fun inject(viewModel: QRScannerViewModel) + fun inject(viewModel: QrScannerViewModel) fun inject(viewModel: ChatListViewModel) fun inject(viewModel: ChatDetailsViewModel) fun inject(viewModel: DataCollectionViewModel) diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt index 05898237f..6d1c1c2f9 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt @@ -137,7 +137,7 @@ class TariBluetoothClient @Inject constructor(val deeplinkHandler: DeeplinkHandl private fun doHandling(string: String): GattStatus { logger.i("contactlessPayment: handle: url: $string") - val handled = runCatching { deeplinkHandler.handle(string) }.getOrNull() + val handled = runCatching { deeplinkHandler.parseDeepLink(string) }.getOrNull() logger.i("contactlessPayment: handle: handled: $handled") diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt index 7a23e07c1..58a9168a0 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt @@ -120,7 +120,7 @@ class TariBluetoothServer @Inject constructor( private fun doHandling(string: String): GattStatus { logger.i("share: handle: url: $string") - val handled = runCatching { deeplinkHandler.handle(string) }.getOrNull() + val handled = runCatching { deeplinkHandler.parseDeepLink(string) }.getOrNull() logger.i("share: handle: handled: $handled") @@ -141,7 +141,7 @@ class TariBluetoothServer @Inject constructor( fun initiateReading() { if (shareChunkedData.isNotEmpty()) return val myWalletAddress = sharedPrefsRepository.walletAddress - val data = deeplinkHandler.getDeeplink( + val data = deeplinkHandler.getDeeplinkString( DeepLink.UserProfile( tariAddress = sharedPrefsRepository.walletAddressBase58.orEmpty(), alias = contactUtil.normalizeAlias( diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt index 016a52353..a9edb918c 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt @@ -49,9 +49,9 @@ import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.infrastructure.backup.BackupManager import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.service.ServiceRestartBroadcastReceiver -import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.startAction -import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.stopAction -import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.stopAndDeleteAction +import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.START_ACTION +import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.STOP_ACTION +import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.STOP_AND_DELETE_ACTION import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.fragment.settings.logs.LogFilesManager import com.tari.android.wallet.util.Constants @@ -131,9 +131,9 @@ class WalletService : Service() { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { startForeground() when (intent.action) { - startAction -> startService() - stopAction -> stopService(startId) - stopAndDeleteAction -> { + START_ACTION -> startService() + STOP_ACTION -> stopService(startId) + STOP_AND_DELETE_ACTION -> { //todo total crutch. Service is auto-creating during the bind func. Need to refactor this first DiContainer.appComponent.inject(this) stopService(startId) diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt index 2a71087c6..e22165ec8 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt @@ -45,18 +45,18 @@ class WalletServiceLauncher( } } - private fun getStartIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = startAction } + private fun getStartIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = START_ACTION } - private fun getStopIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = stopAction } + private fun getStopIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = STOP_ACTION } - private fun getStopAndDeleteIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = stopAndDeleteAction } + private fun getStopAndDeleteIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = STOP_AND_DELETE_ACTION } companion object { // intent actions - const val startAction = "START_SERVICE" - const val stopAction = "STOP_SERVICE" - const val stopAndDeleteAction = "STOP_SERVICE_AND_DELETE_WALLET" + const val START_ACTION = "START_SERVICE" + const val STOP_ACTION = "STOP_SERVICE" + const val STOP_AND_DELETE_ACTION = "STOP_SERVICE_AND_DELETE_WALLET" } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt index 9a7b4ab91..cacedc9de 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt @@ -53,7 +53,7 @@ class WalletAddressViewModel : CommonViewModel() { } private fun findValidEmojiId(query: String): TariWalletAddress? { - return when (val deepLink = deeplinkHandler.handle(query)) { + return when (val deepLink = deeplinkHandler.parseDeepLink(query)) { is DeepLink.Send -> deepLink.walletAddress is DeepLink.UserProfile -> deepLink.tariAddress is DeepLink.Contacts -> deepLink.contacts.firstOrNull()?.tariAddress diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/add/SelectUserContactFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/add/SelectUserContactFragment.kt index 0ff03d6c9..549dd1e14 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/add/SelectUserContactFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/add/SelectUserContactFragment.kt @@ -8,7 +8,7 @@ import com.tari.android.wallet.ui.extension.setVisible import com.tari.android.wallet.ui.extension.string import com.tari.android.wallet.ui.fragment.contactBook.contactSelection.ContactSelectionFragment import com.tari.android.wallet.ui.fragment.contactBook.contactSelection.ContactSelectionViewModel.ContinueButtonEffect -import com.tari.android.wallet.ui.fragment.qr.QRScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource class SelectUserContactFragment : ContactSelectionFragment() { @@ -33,7 +33,7 @@ class SelectUserContactFragment : ContactSelectionFragment() { } override fun startQRCodeActivity() { - QRScannerActivity.startScanner(this, QrScannerSource.TransactionSend) + QrScannerActivity.startScanner(this, QrScannerSource.TransactionSend) } override fun goToNext() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt index 888542caf..f44e6440e 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.daasuu.ei.Ease import com.daasuu.ei.EasingInterpolator import com.tari.android.wallet.R +import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentContactsSelectionBinding import com.tari.android.wallet.extension.launchAndRepeatOnLifecycle @@ -35,6 +36,7 @@ import com.tari.android.wallet.ui.component.clipboardController.ClipboardControl import com.tari.android.wallet.ui.component.tari.toolbar.TariToolbarActionArg import com.tari.android.wallet.ui.extension.gone import com.tari.android.wallet.ui.extension.hideKeyboard +import com.tari.android.wallet.ui.extension.parcelable import com.tari.android.wallet.ui.extension.postDelayed import com.tari.android.wallet.ui.extension.setSelectionToEnd import com.tari.android.wallet.ui.extension.setVisible @@ -46,7 +48,7 @@ import com.tari.android.wallet.ui.fragment.contactBook.contactSelection.ContactS import com.tari.android.wallet.ui.fragment.contactBook.contacts.adapter.ContactListAdapter import com.tari.android.wallet.ui.fragment.contactBook.contacts.adapter.contact.ContactItemViewHolderItem import com.tari.android.wallet.ui.fragment.contactBook.contacts.adapter.contact.ContactlessPaymentItem -import com.tari.android.wallet.ui.fragment.qr.QRScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.util.Constants import com.tari.android.wallet.util.DebugConfig @@ -231,7 +233,7 @@ open class ContactSelectionFragment : CommonFragment(QrScannerActivity.EXTRA_DEEPLINK) ?: return + viewModel.handleDeeplink(qrDeepLink) } } @@ -340,8 +342,8 @@ open class ContactSelectionFragment : CommonFragment deeplink.contacts.firstOrNull()?.tariAddress is DeepLink.Send -> deeplink.walletAddress @@ -135,7 +130,7 @@ class ContactSelectionViewModel : CommonViewModel() { deeplinkBase58?.let { TariWalletAddress.fromBase58OrNull(it) }?.let { walletAddress -> selectedContact.value = ContactDto(FFIContactInfo(walletAddress), uuid = name) _yatState.update { it.copy(yatUser = null) } - } ?: run { logger.e("Wallet address not found for deeplink: $deeplinkString") } + } ?: run { logger.e("Wallet address not found for deeplink: $deeplink") } } fun onContactlessPaymentClick() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt index 49e20a8d0..197963b0e 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt @@ -15,6 +15,7 @@ import androidx.fragment.app.viewModels import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayoutMediator import com.tari.android.wallet.R +import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentContactBookRootBinding import com.tari.android.wallet.extension.observe @@ -23,6 +24,7 @@ import com.tari.android.wallet.ui.common.CommonFragment import com.tari.android.wallet.ui.component.clipboardController.ClipboardController import com.tari.android.wallet.ui.component.tari.toolbar.TariToolbarActionArg import com.tari.android.wallet.ui.extension.hideKeyboard +import com.tari.android.wallet.ui.extension.parcelable import com.tari.android.wallet.ui.extension.postDelayed import com.tari.android.wallet.ui.extension.setVisible import com.tari.android.wallet.ui.extension.showKeyboard @@ -34,7 +36,7 @@ import com.tari.android.wallet.ui.fragment.contactBook.root.share.ShareOptionArg import com.tari.android.wallet.ui.fragment.contactBook.root.share.ShareOptionView import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.ui.fragment.home.navigation.Navigation -import com.tari.android.wallet.ui.fragment.qr.QRScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.util.Constants import java.lang.ref.WeakReference @@ -71,9 +73,9 @@ class ContactBookFragment : CommonFragment(QrScannerActivity.EXTRA_DEEPLINK) ?: return + viewModel.handleDeeplink(qrDeeplink) } } @@ -153,7 +155,7 @@ class ContactBookFragment : CommonFragment deeplink.contacts.firstOrNull()?.tariAddress is DeepLink.Send -> deeplink.walletAddress is DeepLink.UserProfile -> deeplink.tariAddress @@ -127,7 +123,7 @@ class ContactBookViewModel : CommonViewModel() { tariAddress = it.contactInfo.requireWalletAddress().fullBase58, ) } - return deeplinkHandler.getDeeplink(DeepLink.Contacts(contacts)) + return deeplinkHandler.getDeeplinkString(DeepLink.Contacts(contacts)) } private fun setSelectedToShareType(shareType: ShareType) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt index 05a983460..b5cafabe8 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt @@ -160,7 +160,7 @@ class ShareViewModel : CommonViewModel() { } private fun onReceived(data: List) { - deeplinkViewModel.addContacts(DeepLink.Contacts(data)) + deeplinkViewModel.execute(DeepLink.Contacts(data)) } companion object { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt index 35e5c48b7..1b0d897dc 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt @@ -284,7 +284,9 @@ class HomeActivity : CommonActivity() { fun willNotifyAboutNewTx(): Boolean = ui.viewPager.currentItem == INDEX_HOME private fun processIntentDeepLink(intent: Intent) { - deeplinkViewModel.tryToHandle(intent.data?.toString().orEmpty(), false) + intent.data?.toString()?.takeIf { it.isNotEmpty() }?.let { deeplinkString -> + deeplinkViewModel.tryToHandle(deeplinkString, isQrData = false) + } } override fun onDestroy() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt index b5d05b707..83d6a707a 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt @@ -56,7 +56,7 @@ class WalletInfoViewModel : CommonViewModel() { ) val uiState = _uiState.asStateFlow() - private val shareProfileDeeplink = deeplinkHandler.getDeeplink( + private val shareProfileDeeplink = deeplinkHandler.getDeeplinkString( DeepLink.UserProfile( tariAddress = corePrefRepository.walletAddressBase58.orEmpty(), alias = contactUtil.normalizeAlias(uiState.value.alias, corePrefRepository.walletAddress), diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerViewModel.kt deleted file mode 100644 index 1315c5c74..000000000 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerViewModel.kt +++ /dev/null @@ -1,161 +0,0 @@ -package com.tari.android.wallet.ui.fragment.qr - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope -import com.tari.android.wallet.R -import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel -import com.tari.android.wallet.extension.launchOnIo -import com.tari.android.wallet.extension.launchOnMain -import com.tari.android.wallet.model.TariWalletAddress -import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.common.SingleLiveEvent -import com.tari.android.wallet.ui.fragment.home.HomeActivity -import com.tari.android.wallet.util.shortString -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import javax.inject.Inject - -class QRScannerViewModel : CommonViewModel() { - - @Inject - lateinit var deeplinkHandler: DeeplinkHandler - - init { - component.inject(this) - } - - val qrScannerSource: MutableLiveData = MutableLiveData() - - val scanError: MutableLiveData = MutableLiveData(false) - - val alternativeText: MutableLiveData = MutableLiveData("") - - val scannedDeeplink: MutableLiveData = MutableLiveData() - - val navigationBackWithData: SingleLiveEvent = SingleLiveEvent() - - val deeplinkViewModel: DeeplinkViewModel - get() = HomeActivity.instance.get()?.deeplinkViewModel!! - - val proceedScan = SingleLiveEvent() - - fun init(source: QrScannerSource) { - qrScannerSource.postValue(source) - } - - fun onAlternativeApply() { - backPressed.postValue(Unit) - executeWithDelay(deeplinkViewModel) { - deeplinkViewModel.executeRawDeeplink(scannedDeeplink.value!!) - } - } - - private fun executeWithDelay(commonViewModel: CommonViewModel, action: () -> Unit) { - commonViewModel.viewModelScope.launch(Dispatchers.IO) { - delay(500) - commonViewModel.viewModelScope.launch(Dispatchers.Main) { - action() - } - } - } - - fun onAlternativeDeny() { - alternativeText.postValue("") - proceedScan.postValue(Unit) - } - - fun onScanResult(text: String?) { - val deeplink = deeplinkHandler.handle(text.orEmpty()) - if (deeplink == null) { - scanError.postValue(true) - } else { - handleDeeplink(deeplink) - } - } - - private fun handleDeeplink(deepLink: DeepLink) { - scannedDeeplink.postValue(deepLink) - - when (qrScannerSource.value) { - QrScannerSource.None, - QrScannerSource.Home -> setAlternativeText(deepLink) - - QrScannerSource.TransactionSend -> { - when (deepLink) { - is DeepLink.Send, - is DeepLink.UserProfile -> navigateBack(deepLink) - - is DeepLink.Contacts, - is DeepLink.TorBridges, - is DeepLink.AddBaseNode -> setAlternativeText(deepLink) - } - } - - QrScannerSource.AddContact -> { - when (deepLink) { - is DeepLink.UserProfile -> navigateBack(deepLink) - - is DeepLink.Send, - is DeepLink.Contacts, - is DeepLink.TorBridges, - is DeepLink.AddBaseNode -> setAlternativeText(deepLink) - } - } - - QrScannerSource.ContactBook -> { - when (deepLink) { - is DeepLink.Send, - is DeepLink.UserProfile, - is DeepLink.Contacts -> navigateBack(deepLink) - - is DeepLink.TorBridges, - is DeepLink.AddBaseNode -> setAlternativeText(deepLink) - } - } - - QrScannerSource.TorBridges -> { - when (deepLink) { - is DeepLink.Send, - is DeepLink.UserProfile, - is DeepLink.AddBaseNode, - is DeepLink.Contacts -> setAlternativeText(deepLink) - - is DeepLink.TorBridges -> navigateBack(deepLink) - } - } - - null -> Unit - } - } - - private fun setAlternativeText(deepLink: DeepLink) { - launchOnIo { - val text = when (deepLink) { - is DeepLink.Send -> { - val walletAddress = TariWalletAddress.fromBase58OrNull(deepLink.walletAddress) ?: return@launchOnIo - resourceManager.getString(R.string.qr_code_scanner_labels_actions_transaction_send, walletAddress.shortString()) - } - - is DeepLink.UserProfile -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_profile) - is DeepLink.Contacts -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_contacts) - is DeepLink.AddBaseNode -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_base_node_add) - is DeepLink.TorBridges -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_tor_bridges) - } - launchOnMain { - alternativeText.postValue(text) - } - } - } - - private fun navigateBack(deepLink: DeepLink) { - navigationBackWithData.postValue(deeplinkHandler.getDeeplink(deepLink)) - } - - fun onRetry() { - proceedScan.postValue(Unit) - scanError.postValue(false) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt similarity index 82% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerActivity.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt index b228c184f..283514515 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QRScannerActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt @@ -49,13 +49,13 @@ import com.budiyev.android.codescanner.ErrorCallback import com.budiyev.android.codescanner.ScanMode import com.google.zxing.BarcodeFormat import com.tari.android.wallet.R +import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.databinding.ActivityQrScannerBinding import com.tari.android.wallet.di.DiContainer.appComponent -import com.tari.android.wallet.extension.observe +import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.ui.common.CommonActivity import com.tari.android.wallet.ui.component.tari.toast.TariToast import com.tari.android.wallet.ui.component.tari.toast.TariToastArgs -import com.tari.android.wallet.ui.extension.serializable import com.tari.android.wallet.ui.extension.setVisible /** @@ -63,7 +63,7 @@ import com.tari.android.wallet.ui.extension.setVisible * * @author The Tari Development Team */ -class QRScannerActivity : CommonActivity() { +class QrScannerActivity : CommonActivity() { companion object { /** @@ -72,20 +72,20 @@ class QRScannerActivity : CommonActivity(QR_DATA_SOURCE) - viewModel.init(data ?: QrScannerSource.None) - subscribeUI() setupUi() @@ -113,16 +110,18 @@ class QRScannerActivity : CommonActivity + ui.errorContainer.setVisible(uiState.scanError) + ui.alternativeText.text = uiState.alternativeText + ui.alternativeContainer.setVisible(uiState.alternativeText.isNotEmpty()) } - observe(navigationBackWithData) { doNavigationBack(it) } - - observe(proceedScan) { codeScanner.startPreview() } + collectFlow(effect) { effect -> + when (effect) { + is QrScannerModel.Effect.FinishWithResult -> finishWithResult(effect.deepLink) + is QrScannerModel.Effect.ProceedScan -> codeScanner.startPreview() + } + } } private fun setupUi() = with(ui) { @@ -154,9 +153,9 @@ class QRScannerActivity : CommonActivity = _uiState.asStateFlow() + + private val _effect = EffectChannelFlow() + val effect: Flow = _effect.flow + + private val qrScannerSource: QrScannerSource = savedState.get(EXTRA_QR_DATA_SOURCE) ?: QrScannerSource.None + + fun onAlternativeApply() { + backPressed.postValue(Unit) + executeWithDelay(deeplinkViewModel.viewModelScope) { + deeplinkViewModel.executeRawDeeplink(uiState.value.scannedDeeplink!!) + } + } + + private fun executeWithDelay(scope: CoroutineScope, action: () -> Unit) { + scope.launch(Dispatchers.IO) { + delay(500) + scope.launch(Dispatchers.Main) { + action() + } + } + } + + fun onAlternativeDeny() { + launchOnMain { + _uiState.update { it.copy(alternativeText = "") } + _effect.send(QrScannerModel.Effect.ProceedScan) + } + } + + fun onScanResult(text: String?) { + val deeplink = deeplinkHandler.parseDeepLink(text.orEmpty()) + if (deeplink == null) { + _uiState.update { it.copy(scanError = true) } + } else { + handleDeeplink(deeplink) + } + } + + private fun handleDeeplink(deepLink: DeepLink) { + _uiState.update { it.copy(scannedDeeplink = deepLink) } + + when (qrScannerSource) { + QrScannerSource.None, + QrScannerSource.Home -> setAlternativeText(deepLink) + + QrScannerSource.TransactionSend -> { + when (deepLink) { + is DeepLink.Send, + is DeepLink.UserProfile, + is DeepLink.PaperWallet -> returnResult(deepLink) + + is DeepLink.Contacts, + is DeepLink.TorBridges, + is DeepLink.AddBaseNode -> setAlternativeText(deepLink) + } + } + + QrScannerSource.AddContact -> { + when (deepLink) { + is DeepLink.UserProfile, + is DeepLink.PaperWallet -> returnResult(deepLink) + + is DeepLink.Send, + is DeepLink.Contacts, + is DeepLink.TorBridges, + is DeepLink.AddBaseNode -> setAlternativeText(deepLink) + } + } + + QrScannerSource.ContactBook -> { + when (deepLink) { + is DeepLink.Send, + is DeepLink.UserProfile, + is DeepLink.Contacts, + is DeepLink.PaperWallet -> returnResult(deepLink) + + is DeepLink.TorBridges, + is DeepLink.AddBaseNode -> setAlternativeText(deepLink) + + } + } + + QrScannerSource.TorBridges -> { + when (deepLink) { + is DeepLink.Send, + is DeepLink.UserProfile, + is DeepLink.AddBaseNode, + is DeepLink.Contacts, + is DeepLink.PaperWallet -> setAlternativeText(deepLink) + + is DeepLink.TorBridges -> returnResult(deepLink) + } + } + + QrScannerSource.PaperWallet -> { + when (deepLink) { + is DeepLink.Send, + is DeepLink.UserProfile, + is DeepLink.Contacts, + is DeepLink.TorBridges, + is DeepLink.AddBaseNode -> _uiState.update { it.copy(scanError = true) } + + is DeepLink.PaperWallet -> returnResult(deepLink) + } + } + } + } + + private fun setAlternativeText(deepLink: DeepLink) { + val text = when (deepLink) { + is DeepLink.Send -> { + val walletAddress = TariWalletAddress.fromBase58OrNull(deepLink.walletAddress) ?: return + resourceManager.getString(R.string.qr_code_scanner_labels_actions_transaction_send, walletAddress.shortString()) + } + + is DeepLink.UserProfile -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_profile) + is DeepLink.Contacts -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_contacts) + is DeepLink.AddBaseNode -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_base_node_add) + is DeepLink.TorBridges -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_tor_bridges) + is DeepLink.PaperWallet -> resourceManager.getString(R.string.qr_code_scanner_labels_actions_paper_wallet) // should never show. Show PW dialog instead + } + _uiState.update { it.copy(alternativeText = text) } + } + + private fun returnResult(deepLink: DeepLink) { + launchOnMain { _effect.send(QrScannerModel.Effect.FinishWithResult(deepLink)) } + } + + fun onRetry() { + launchOnMain { + _effect.send(QrScannerModel.Effect.ProceedScan) + _uiState.update { it.copy(scanError = false) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/activity/WalletRestoreActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/activity/WalletRestoreActivity.kt index 0a579043b..4e8ab46d0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/activity/WalletRestoreActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/activity/WalletRestoreActivity.kt @@ -37,7 +37,6 @@ import android.content.Intent import android.os.Bundle import androidx.activity.viewModels import com.tari.android.wallet.R -import com.tari.android.wallet.application.MigrationManager import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository import com.tari.android.wallet.databinding.ActivityWalletBackupBinding @@ -58,15 +57,6 @@ class WalletRestoreActivity : CommonActivity() { @@ -33,7 +35,7 @@ class CustomTorBridgesFragment : CommonFragment(QrScannerActivity.EXTRA_DEEPLINK) ?: return + viewModel.handleQrCode(qrDeepLink) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt index fcfb047ab..9a2bd269a 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt @@ -65,10 +65,9 @@ class CustomTorBridgesViewModel : CommonViewModel() { backPressed.postValue(Unit) } - fun handleQrCode(input: String) { - val deeplink = deeplinkHandler.handle(input) + fun handleQrCode(deeplink: DeepLink) { if (deeplink is DeepLink.TorBridges) { - val text = deeplinkHandler.getDeeplink(deeplink) + val text = deeplinkHandler.getDeeplinkString(deeplink) this.text.postValue(text) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt index cf8dedddb..aea32d307 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt @@ -44,6 +44,7 @@ import android.view.ViewGroup import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import com.tari.android.wallet.R +import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentHomeBinding import com.tari.android.wallet.event.EventBus @@ -55,9 +56,10 @@ import com.tari.android.wallet.ui.common.recyclerView.AdapterFactory import com.tari.android.wallet.ui.common.recyclerView.CommonAdapter import com.tari.android.wallet.ui.common.recyclerView.CommonViewHolderItem import com.tari.android.wallet.ui.component.networkStateIndicator.ConnectionIndicatorViewModel +import com.tari.android.wallet.ui.extension.parcelable import com.tari.android.wallet.ui.extension.setVisible import com.tari.android.wallet.ui.fragment.home.navigation.Navigation -import com.tari.android.wallet.ui.fragment.qr.QRScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.ui.fragment.tx.adapter.TxListHomeViewHolder import com.tari.android.wallet.ui.fragment.tx.questionMark.QuestionMarkViewModel @@ -138,7 +140,7 @@ class HomeFragment : CommonFragment( @SuppressLint("ClickableViewAccessibility") private fun setupUI() = with(ui) { viewAllTxsButton.setOnClickListener { viewModel.navigation.postValue(Navigation.TxListNavigation.HomeTransactionHistory) } - qrCodeButton.setOnClickListener { QRScannerActivity.startScanner(requireActivity(), QrScannerSource.Home) } + qrCodeButton.setOnClickListener { QrScannerActivity.startScanner(requireActivity(), QrScannerSource.Home) } transactionsRecyclerView.adapter = adapter adapter.setClickListener(CommonAdapter.ItemClickListener { viewModel.processItemClick(it) }) transactionsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) @@ -155,9 +157,9 @@ class HomeFragment : CommonFragment( @Deprecated("Deprecated in Java") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == QRScannerActivity.REQUEST_QR_SCANNER && resultCode == Activity.RESULT_OK && data != null) { - val qrData = data.getStringExtra(QRScannerActivity.EXTRA_QR_DATA) ?: return - deeplinkViewModel.tryToHandle(qrData) + if (requestCode == QrScannerActivity.REQUEST_QR_SCANNER && resultCode == Activity.RESULT_OK && data != null) { + val qrDeepLink = data.parcelable(QrScannerActivity.EXTRA_DEEPLINK) ?: return + deeplinkViewModel.execute(qrDeepLink) } } diff --git a/app/src/main/res/layout/activity_qr_scanner.xml b/app/src/main/res/layout/activity_qr_scanner.xml index cfa6e6231..980aee9cb 100644 --- a/app/src/main/res/layout/activity_qr_scanner.xml +++ b/app/src/main/res/layout/activity_qr_scanner.xml @@ -45,7 +45,7 @@ app:layout_constraintDimensionRatio="3:1" app:layout_constraintTop_toBottomOf="@id/qr_close_view"> - - - - + app:customFont="heavy" /> + - - - - + android:gravity="center_horizontal" + android:orientation="vertical"> - + - + + + + + tools:visibility="visible"> - - - - - - - - - - - + android:orientation="vertical"> - - - + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f3493a88..c7c57dccb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -276,6 +276,7 @@ This QR code contains contacts information. \nWould you like to add its to your contact book? This QR code contains contact information. \nWould you like to add it to your contact book? This QR code contains a custom bridge.\nWould you like to save it to the app? + This QR code contains a paper wallet.\nWould you like to save it to the app? From 5dcb423c07ad8f14a9cad466fe54db56aa1d7d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sat, 5 Oct 2024 10:15:51 +0200 Subject: [PATCH 09/32] Fix the JVM target version --- app/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 9aae3ab3b..ed58afe1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,6 +95,16 @@ android { targetCompatibility JavaVersion.VERSION_17 } + kotlinOptions { + jvmTarget = "17" + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } + buildFeatures { viewBinding true aidl true From a37acbad8093a252bbb28748ca77990b9c52a260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sat, 5 Oct 2024 13:20:31 +0200 Subject: [PATCH 10/32] Add the paper wallet option to the recovery options --- .../backup/BackupPrefRepository.kt | 94 ++++++++++----- .../ChooseRestoreOptionFragment.kt | 28 +++-- .../ChooseRestoreOptionViewModel.kt | 68 +++++++---- .../layout/fragment_choose_restore_option.xml | 111 ++++++++++-------- .../main/res/layout/view_restore_option.xml | 65 +++++----- app/src/main/res/values/strings.xml | 7 ++ 6 files changed, 224 insertions(+), 149 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt index 34c85c002..6cdf7f8a6 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt @@ -6,6 +6,7 @@ import android.net.Uri import com.dropbox.core.oauth.DbxCredential import com.tari.android.wallet.BuildConfig import com.tari.android.wallet.data.sharedPrefs.CommonPrefRepository +import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefGsonNullableDelegate import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefStringSecuredDelegate import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository @@ -24,30 +25,62 @@ class BackupPrefRepository @Inject constructor( networkRepository: NetworkPrefRepository ) : CommonPrefRepository(networkRepository) { - var localFileOption: BackupOptionDto? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.localFileOptionsKey), BackupOptionDto::class.java) - - var googleDriveOption: BackupOptionDto? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.googleDriveOptionKey), BackupOptionDto::class.java) - - var dropboxOption: BackupOptionDto? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.dropboxOptionsKey), BackupOptionDto::class.java) - - var dropboxCredential: DbxCredential? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.dropboxCredentialKey), DbxCredential::class.java) - - var backupPassword: String? by SharedPrefStringSecuredDelegate(context, sharedPrefs, this, formatKey(Keys.backupPassword)) - - var localBackupFolderURI: Uri? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.localBackupFolderURI), Uri::class.java) - - var restoredTxs: BackupUtxos? by SharedPrefGsonNullableDelegate(sharedPrefs, this, formatKey(Keys.lastRestoredTxs), BackupUtxos::class.java, null) - - init { - localFileOption = localFileOption ?: BackupOptionDto(BackupOptions.Local) - googleDriveOption = googleDriveOption ?: BackupOptionDto(BackupOptions.Google) - } + private var localFileOption: BackupOptionDto by SharedPrefGsonDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.LOCAL_FILE_OPTIONS_KEY), + type = BackupOptionDto::class.java, + defValue = BackupOptionDto(BackupOptions.Local), + ) + + private var googleDriveOption: BackupOptionDto by SharedPrefGsonDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.GOOGLE_DRIVE_OPTION_KEY), + type = BackupOptionDto::class.java, + defValue = BackupOptionDto(BackupOptions.Google), + ) + + var dropboxOption: BackupOptionDto? by SharedPrefGsonNullableDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.DROPBOX_OPTIONS_KEY), + type = BackupOptionDto::class.java, + ) + + var dropboxCredential: DbxCredential? by SharedPrefGsonNullableDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.DROPBOX_CREDENTIAL_KEY), + type = DbxCredential::class.java + ) + + var backupPassword: String? by SharedPrefStringSecuredDelegate( + context = context, + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.BACKUP_PASSWORD), + ) + + var localBackupFolderURI: Uri? by SharedPrefGsonNullableDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.LOCAL_BACKUP_FOLDER_URI), + type = Uri::class.java, + ) + + var restoredTxs: BackupUtxos? by SharedPrefGsonNullableDelegate( + prefs = sharedPrefs, + commonRepository = this, + name = formatKey(Keys.LAST_RESTORED_TXS), + type = BackupUtxos::class.java, + ) val getOptionList: List get() = if (BuildConfig.FLAVOR == Constants.Build.privacyFlavor) { - listOfNotNull(localFileOption).toList() + listOfNotNull(localFileOption) } else { - listOfNotNull(googleDriveOption, dropboxOption).toList() + listOfNotNull(googleDriveOption, dropboxOption) } fun clear() { @@ -72,18 +105,15 @@ class BackupPrefRepository @Inject constructor( BackupOptions.Dropbox -> dropboxOption } - object Keys { - const val googleDriveOptionKey = "tari_wallet_google_drive_backup_options" - const val localFileOptionsKey = "tari_wallet_local_file_backup_options" - const val dropboxOptionsKey = "tari_wallet_dropbox_backup_options" - const val dropboxCredentialKey = "tari_wallet_dropbox_credential_key" - const val backupPassword = "tari_wallet_last_next_alarm_time" - const val localBackupFolderURI = "tari_wallet_local_backup_folder_uri" - const val lastBackupDialogShownTime = "last_shown_time_key" - const val lastRestoredTxs = "tari_wallet_restored_txs" - } - companion object { - const val delayTimeInMinutes = 5 + object Keys { + const val GOOGLE_DRIVE_OPTION_KEY = "tari_wallet_google_drive_backup_options" + const val LOCAL_FILE_OPTIONS_KEY = "tari_wallet_local_file_backup_options" + const val DROPBOX_OPTIONS_KEY = "tari_wallet_dropbox_backup_options" + const val DROPBOX_CREDENTIAL_KEY = "tari_wallet_dropbox_credential_key" + const val BACKUP_PASSWORD = "tari_wallet_last_next_alarm_time" + const val LOCAL_BACKUP_FOLDER_URI = "tari_wallet_local_backup_folder_uri" + const val LAST_RESTORED_TXS = "tari_wallet_restored_txs" + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt index 8a9d94353..96b9d2147 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt @@ -32,6 +32,7 @@ */ package com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -39,10 +40,12 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.children import androidx.fragment.app.viewModels +import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.databinding.FragmentChooseRestoreOptionBinding import com.tari.android.wallet.extension.observe import com.tari.android.wallet.ui.common.CommonFragment -import com.tari.android.wallet.ui.fragment.home.navigation.Navigation +import com.tari.android.wallet.ui.extension.parcelable +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.option.RecoveryOptionView import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions @@ -71,16 +74,17 @@ class ChooseRestoreOptionFragment : CommonFragment(QrScannerActivity.EXTRA_DEEPLINK) ?: return + viewModel.handleDeeplink(qrDeepLink) + } else { + viewModel.onActivityResult(requestCode, resultCode, data) + } } private fun setupUI() = with(ui) { - restoreWithRecoveryPhraseCtaView.setOnClickListener { viewModel.navigation.postValue(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase) } - } - - private fun startRecovery(options: BackupOptions) { - viewModel.startRestore(options) - viewModel.backupManager.setupStorage(options, this) + restoreWithRecoveryPhraseCtaView.setOnClickListener { viewModel.onRecoveryPhraseClicked() } + restoreWithPaperWalletCtaView.setOnClickListener { viewModel.onPaperWalletClicked(this@ChooseRestoreOptionFragment) } } private fun observeUI() = with(viewModel) { @@ -93,7 +97,12 @@ class ChooseRestoreOptionFragment : CommonFragment { logger.i("Restore failed: wallet start failed") - viewModelScope.launch(Dispatchers.Main) { + launchOnMain { walletServiceLauncher.stopAndDelete() } val cause = WalletError(exception.cause) @@ -164,31 +181,36 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { _state.postValue(ChooseRestoreOptionState.EndProgress(backupManager.currentOption!!)) } + private fun showPaperWalletDialog(seedWords: List) { + // TODO + } + private fun showBackupFileNotFoundDialog() { - showModularDialog( - SimpleDialogArgs( - title = resourceManager.getString(R.string.restore_wallet_error_title), - description = resourceManager.getString(R.string.restore_wallet_error_file_not_found), - onClose = { backPressed.call() }, - ).getModular(resourceManager) + showSimpleDialog( + title = resourceManager.getString(R.string.restore_wallet_error_title), + description = resourceManager.getString(R.string.restore_wallet_error_file_not_found), + onClose = { backPressed.call() }, ) } private fun showRestoreFailedDialog(message: String? = null) { - showModularDialog( - SimpleDialogArgs( - title = resourceManager.getString(R.string.restore_wallet_error_title), - description = resourceManager.getString(R.string.restore_wallet_error_desc, message.orEmpty()) - ).getModular(resourceManager) + showSimpleDialog( + title = resourceManager.getString(R.string.restore_wallet_error_title), + description = resourceManager.getString(R.string.restore_wallet_error_desc, message.orEmpty()), ) } private fun showAuthFailedDialog() { - showModularDialog( - SimpleDialogArgs( - title = resourceManager.getString(R.string.restore_wallet_error_title), - description = resourceManager.getString(R.string.back_up_wallet_storage_setup_error_desc), - ).getModular(resourceManager) + showSimpleDialog( + title = resourceManager.getString(R.string.restore_wallet_error_title), + description = resourceManager.getString(R.string.back_up_wallet_storage_setup_error_desc), + ) + } + + private fun showInvalidQrDialog() { + showSimpleDialog( + title = resourceManager.getString(R.string.restore_wallet_invalid_qr_code), + description = resourceManager.getString(R.string.restore_wallet_invalid_qr_code_description), ) } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_choose_restore_option.xml b/app/src/main/res/layout/fragment_choose_restore_option.xml index afa1d2b10..a6da02306 100644 --- a/app/src/main/res/layout/fragment_choose_restore_option.xml +++ b/app/src/main/res/layout/fragment_choose_restore_option.xml @@ -1,6 +1,5 @@ - - - - - - - - - + android:layout_height="wrap_content" + android:orientation="vertical" /> - + - + - - + - + + - + - + + + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/view_restore_option.xml b/app/src/main/res/layout/view_restore_option.xml index aed34420d..01d7c3fe7 100644 --- a/app/src/main/res/layout/view_restore_option.xml +++ b/app/src/main/res/layout/view_restore_option.xml @@ -1,6 +1,5 @@ - - - - - + - + - + - - - - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7c57dccb..fc4068a0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -545,6 +545,7 @@ Restore with Google Drive Restore with Local files Restore with Dropbox + Restore from paper wallet Back Up With Seed Phrase @@ -571,6 +572,12 @@ Restore Wallet @string/enter_backup_password_password_match_error You recovered some funds + Invalid QR Code + The QR code you scanned does not contain valid paper wallet seeds + Paper Wallet Detected + You’ve scanned a paper wallet!\nWhat would you like to do? + Restore the wallet + Don\'t restore right now Restore With Seed Phrase From 13ca7832c5c2203ebba1e0dc5dbbe96f528c29b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sat, 5 Oct 2024 16:54:16 +0200 Subject: [PATCH 11/32] Make DeeplinkManager from DeeplinkViewModel and make it a singleton --- .../application/deeplinks/DeeplinkHandler.kt | 3 + .../application/deeplinks/DeeplinkManager.kt | 166 ++++++++++++++++++ .../deeplinks/DeeplinkViewModel.kt | 153 ---------------- .../android/wallet/di/ApplicationComponent.kt | 2 - .../android/wallet/ui/common/DialogManager.kt | 6 + .../ui/dialog/modular/ModularDialogArgs.kt | 2 + .../ContactSelectionFragment.kt | 8 +- .../ContactSelectionViewModel.kt | 11 ++ .../contactBook/root/ContactBookFragment.kt | 4 - .../contactBook/root/ShareViewModel.kt | 10 +- .../wallet/ui/fragment/home/HomeActivity.kt | 19 +- .../ui/fragment/qr/QrScannerActivity.kt | 3 +- .../ui/fragment/qr/QrScannerViewModel.kt | 29 ++- .../wallet/ui/fragment/tx/HomeFragment.kt | 7 +- .../ui/fragment/tx/HomeFragmentViewModel.kt | 10 ++ 15 files changed, 228 insertions(+), 205 deletions(-) create mode 100644 app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt delete mode 100644 app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt index 70ad88833..405f37cd0 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt @@ -8,6 +8,9 @@ import java.net.URLDecoder import javax.inject.Inject import javax.inject.Singleton + +// TODO DeepLinkParser?? +// TODO move to deeplinkManager ?? @Singleton class DeeplinkHandler @Inject constructor(private val networkRepository: NetworkPrefRepository) { diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt new file mode 100644 index 000000000..4b49b3136 --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -0,0 +1,166 @@ +package com.tari.android.wallet.application.deeplinks + +import android.content.Context +import com.tari.android.wallet.R +import com.tari.android.wallet.application.baseNodes.BaseNodesManager +import com.tari.android.wallet.application.walletManager.WalletManager +import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto +import com.tari.android.wallet.data.sharedPrefs.tor.TorPrefRepository +import com.tari.android.wallet.di.ApplicationScope +import com.tari.android.wallet.model.TariWalletAddress +import com.tari.android.wallet.ui.common.DialogManager +import com.tari.android.wallet.ui.common.domain.ResourceManager +import com.tari.android.wallet.ui.dialog.confirm.ConfirmDialogArgs +import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs +import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs.DialogId +import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle +import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule +import com.tari.android.wallet.ui.fragment.contactBook.data.ContactsRepository +import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto +import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.FFIContactInfo +import com.tari.android.wallet.ui.fragment.home.navigation.Navigation +import com.tari.android.wallet.ui.fragment.home.navigation.TariNavigator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DeeplinkManager @Inject constructor( + private val baseNodesManager: BaseNodesManager, + private val contactRepository: ContactsRepository, + private val torSharedRepository: TorPrefRepository, + private val resourceManager: ResourceManager, + private val dialogManager: DialogManager, + private val walletManager: WalletManager, + private val navigator: TariNavigator, + @ApplicationScope private val applicationScope: CoroutineScope, +) { + + /** + * Executes the given deeplink, but first shows a confirmation dialog. + */ + fun execute(context: Context, deeplink: DeepLink, isQrData: Boolean = true) { + when (deeplink) { + is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink, isQrData) + is DeepLink.Contacts -> showAddContactsDialog(context, deeplink, isQrData) + is DeepLink.Send -> sendAction(deeplink, isQrData) + is DeepLink.UserProfile -> addUserProfile(context, deeplink, isQrData) + is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) + } + } + + /** + * Executes the given deeplink without showing a confirmation dialog. + */ + fun executeRawDeeplink(context: Context, deeplink: DeepLink, isQrData: Boolean = true) { + when (deeplink) { + is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink) + is DeepLink.Contacts -> addContactsAction(deeplink.data(), isQrData) + is DeepLink.Send -> sendAction(deeplink, isQrData) + is DeepLink.UserProfile -> addContactsAction(deeplink.data()?.let { listOf(it) } ?: emptyList(), isQrData) + is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) + } + } + + private fun showAddBaseNodeDialog(context: Context, deeplink: DeepLink.AddBaseNode, isQrData: Boolean = true) { + val baseNode = deeplink.data() + dialogManager.replace( + context = context, + args = ConfirmDialogArgs( + dialogId = DialogId.DEEPLINK_ADD_BASE_NODE, + title = resourceManager.getString(R.string.home_custom_base_node_title), + description = resourceManager.getString(R.string.home_custom_base_node_description), + cancelButtonText = resourceManager.getString(R.string.home_custom_base_node_no_button), + confirmButtonText = resourceManager.getString(R.string.common_lets_do_it), + onConfirm = { + dialogManager.dismiss(DialogId.DEEPLINK_ADD_BASE_NODE) + addBaseNodeAction(baseNode, isQrData) + }, + ).getModular(baseNode, resourceManager), + ) + } + + private fun addUserProfile(context: Context, deeplink: DeepLink.UserProfile, isQrData: Boolean) { + val contact = DeepLink.Contacts( + listOf( + DeepLink.Contacts.DeeplinkContact( + alias = deeplink.alias, + tariAddress = deeplink.tariAddress, + ) + ) + ) + showAddContactsDialog(context, contact, isQrData) + } + + private fun showAddContactsDialog(context: Context, deeplink: DeepLink.Contacts, isQrData: Boolean = true) { + val contactDtos = deeplink.data() + if (contactDtos.isEmpty()) return + val names = contactDtos.joinToString(", ") { it.contactInfo.getAlias().trim() } + dialogManager.replace( + context = context, + args = ModularDialogArgs( + dialogId = DialogId.DEEPLINK_ADD_CONTACTS, + modules = listOf( + HeadModule(resourceManager.getString(R.string.contact_deeplink_title)), + BodyModule(resourceManager.getString(R.string.contact_deeplink_message, contactDtos.size.toString()) + ". " + names), + ButtonModule(resourceManager.getString(R.string.common_confirm), ButtonStyle.Normal) { + addContactsAction(contactDtos, isQrData) + dialogManager.dismiss(DialogId.DEEPLINK_ADD_CONTACTS) + }, + ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close) + ), + ) + ) + } + + private fun addTorBridges(deeplink: DeepLink.TorBridges, isQrData: Boolean) { + deeplink.torConfigurations.forEach { + torSharedRepository.addTorBridgeConfiguration(it) + } + } + + private fun showPaperWalletDialog(deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { + // TODO + } + + private fun DeepLink.AddBaseNode.data(): BaseNodeDto = BaseNodeDto.fromDeeplink(this) + + private fun DeepLink.Contacts.data(): List = this.contacts.mapNotNull { + runCatching { + val tariWalletAddress = TariWalletAddress.fromBase58(it.tariAddress) + ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = it.alias)) + }.getOrNull() + } + + private fun DeepLink.Send.data(): ContactDto? = runCatching { + val tariWalletAddress = TariWalletAddress.fromBase58(this.walletAddress) + ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = "")) + }.getOrNull() + + private fun DeepLink.UserProfile.data(): ContactDto? = runCatching { + val tariWalletAddress = TariWalletAddress.fromBase58(this.tariAddress) + ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = this.alias)) + }.getOrNull() + + private fun addContactsAction(contacts: List, isQrData: Boolean) { + applicationScope.launch(Dispatchers.IO) { + contactRepository.addContactList(contacts) + } + } + + private fun sendAction(deeplink: DeepLink.Send, isQrData: Boolean) { + navigator.navigate(Navigation.TxListNavigation.ToSendWithDeeplink(deeplink)) + } + + private fun addBaseNodeAction(baseNodeDto: BaseNodeDto, isQrData: Boolean) { + baseNodesManager.addUserBaseNode(baseNodeDto) + baseNodesManager.setBaseNode(baseNodeDto) + walletManager.syncBaseNode() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt deleted file mode 100644 index 569317a20..000000000 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkViewModel.kt +++ /dev/null @@ -1,153 +0,0 @@ -package com.tari.android.wallet.application.deeplinks - -import androidx.lifecycle.viewModelScope -import com.tari.android.wallet.R -import com.tari.android.wallet.application.baseNodes.BaseNodesManager -import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto -import com.tari.android.wallet.data.sharedPrefs.tor.TorPrefRepository -import com.tari.android.wallet.model.TariWalletAddress -import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.dialog.confirm.ConfirmDialogArgs -import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule -import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule -import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle -import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule -import com.tari.android.wallet.ui.fragment.contactBook.data.ContactsRepository -import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto -import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.FFIContactInfo -import com.tari.android.wallet.ui.fragment.home.navigation.Navigation -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import javax.inject.Inject - -class DeeplinkViewModel : CommonViewModel() { - - @Inject - lateinit var baseNodesManager: BaseNodesManager - - @Inject - lateinit var contactRepository: ContactsRepository - - @Inject - lateinit var deeplinkHandler: DeeplinkHandler - - @Inject - lateinit var torSharedRepository: TorPrefRepository - - init { - component.inject(this) - } - - fun tryToHandle(qrData: String, isQrData: Boolean = true) { - deeplinkHandler.parseDeepLink(qrData)?.let { execute(it, isQrData) } - } - - fun execute(deeplink: DeepLink, isQrData: Boolean = true) { - when (deeplink) { - is DeepLink.AddBaseNode -> addBaseNode(deeplink, isQrData) - is DeepLink.Contacts -> addContacts(deeplink, isQrData) - is DeepLink.Send -> sendAction(deeplink, isQrData) - is DeepLink.UserProfile -> addUserProfile(deeplink, isQrData) - is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) - } - } - - fun executeRawDeeplink(deeplink: DeepLink, isQrData: Boolean = true) { - when (deeplink) { - is DeepLink.AddBaseNode -> addBaseNode(deeplink) - is DeepLink.Contacts -> addContactsAction(getData(deeplink), isQrData) - is DeepLink.Send -> sendAction(deeplink, isQrData) - is DeepLink.UserProfile -> addContactsAction(getData(deeplink)?.let { listOf(it) } ?: listOf(), isQrData) - is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) - } - } - - private fun addBaseNode(deeplink: DeepLink.AddBaseNode, isQrData: Boolean = true) { - val baseNode = getData(deeplink) - val args = ConfirmDialogArgs( - title = resourceManager.getString(R.string.home_custom_base_node_title), - description = resourceManager.getString(R.string.home_custom_base_node_description), - cancelButtonText = resourceManager.getString(R.string.home_custom_base_node_no_button), - confirmButtonText = resourceManager.getString(R.string.common_lets_do_it), - onConfirm = { - hideDialog() - addBaseNodeAction(baseNode, isQrData) - }, - ).getModular(baseNode, resourceManager) - showModularDialog(args) - } - - private fun addUserProfile(deeplink: DeepLink.UserProfile, isQrData: Boolean) { - val contact = DeepLink.Contacts( - listOf( - DeepLink.Contacts.DeeplinkContact( - alias = deeplink.alias, - tariAddress = deeplink.tariAddress, - ) - ) - ) - addContacts(contact, isQrData) - } - - private fun addContacts(contacts: DeepLink.Contacts, isQrData: Boolean = true) { - val contactDtos = getData(contacts) - if (contactDtos.isEmpty()) return - val names = contactDtos.joinToString(", ") { it.contactInfo.getAlias().trim() } - showModularDialog( - HeadModule(resourceManager.getString(R.string.contact_deeplink_title)), - BodyModule(resourceManager.getString(R.string.contact_deeplink_message, contactDtos.size.toString()) + ". " + names), - ButtonModule(resourceManager.getString(R.string.common_confirm), ButtonStyle.Normal) { - addContactsAction(contactDtos, isQrData) - hideDialog() - }, - ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close) - ) - } - - private fun addTorBridges(deeplink: DeepLink.TorBridges, isQrData: Boolean) { - deeplink.torConfigurations.forEach { - torSharedRepository.addTorBridgeConfiguration(it) - } - } - - private fun showPaperWalletDialog(deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { - showNotReadyYetDialog() - } - - private fun getData(deeplink: DeepLink.AddBaseNode): BaseNodeDto = BaseNodeDto.fromDeeplink(deeplink) - - private fun getData(deeplink: DeepLink.Contacts): List = deeplink.contacts.mapNotNull { - runCatching { - val tariWalletAddress = TariWalletAddress.fromBase58(it.tariAddress) - ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = it.alias)) - }.getOrNull() - } - - private fun getData(deeplink: DeepLink.Send): ContactDto? = runCatching { - val tariWalletAddress = TariWalletAddress.fromBase58(deeplink.walletAddress) - ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = "")) - }.getOrNull() - - private fun getData(userProfile: DeepLink.UserProfile): ContactDto? = runCatching { - val tariWalletAddress = TariWalletAddress.fromBase58(userProfile.tariAddress) - ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = userProfile.alias)) - }.getOrNull() - - private fun addContactsAction(contacts: List, isQrData: Boolean) { - viewModelScope.launch(Dispatchers.IO) { - contactRepository.addContactList(contacts) - } - } - - private fun sendAction(deeplink: DeepLink.Send, isQrData: Boolean) { - navigation.postValue(Navigation.TxListNavigation.ToSendWithDeeplink(deeplink)) - } - - private fun addBaseNodeAction(baseNodeDto: BaseNodeDto, isQrData: Boolean) { - baseNodesManager.addUserBaseNode(baseNodeDto) - baseNodesManager.setBaseNode(baseNodeDto) - walletManager.syncBaseNode() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt index c5d3db3b5..abb73e4e1 100644 --- a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt +++ b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt @@ -34,7 +34,6 @@ package com.tari.android.wallet.di import android.content.ClipboardManager import com.tari.android.wallet.application.TariWalletApplication -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager import com.tari.android.wallet.service.service.WalletService import com.tari.android.wallet.ui.common.CommonViewModel @@ -162,7 +161,6 @@ interface ApplicationComponent { fun inject(viewModel: AddAmountViewModel) fun inject(viewModel: TorBridgesSelectionViewModel) fun inject(viewModel: CustomTorBridgesViewModel) - fun inject(viewModel: DeeplinkViewModel) fun inject(viewModel: LocalAuthViewModel) fun inject(viewModel: CreateWalletViewModel) fun inject(viewModel: IntroductionViewModel) diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt index 35d320858..8251a8ad0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt @@ -1,6 +1,8 @@ package com.tari.android.wallet.ui.common +import android.content.Context import com.tari.android.wallet.ui.dialog.modular.ModularDialog +import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs.DialogId import javax.inject.Inject import javax.inject.Singleton @@ -20,6 +22,10 @@ class DialogManager @Inject constructor() { } } + fun replace(context: Context, args: ModularDialogArgs) { + replace(ModularDialog(context, args)) + } + /** * Dismisses the dialog with the given dialogId. If dialogId is [DialogId.NO_ID], the last dialog in the queue will be dismissed. */ diff --git a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt index 1585905f6..3067fc4d6 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt @@ -13,5 +13,7 @@ data class ModularDialogArgs( const val CONNECTION_STATUS = 601 const val DEBUG_MENU = 602 const val SCREEN_RECORDING = 603 + const val DEEPLINK_ADD_BASE_NODE = 604 + const val DEEPLINK_ADD_CONTACTS = 605 } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt index f44e6440e..d3990de8d 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt @@ -23,7 +23,6 @@ import com.daasuu.ei.Ease import com.daasuu.ei.EasingInterpolator import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentContactsSelectionBinding import com.tari.android.wallet.extension.launchAndRepeatOnLifecycle import com.tari.android.wallet.extension.observe @@ -62,8 +61,6 @@ open class ContactSelectionFragment : CommonFragment Boolean = { true } val selectedContact = MutableLiveData() @@ -207,6 +212,12 @@ class ContactSelectionViewModel : CommonViewModel() { } } + fun parseDeeplink(context: Context, deeplinkString: String) { + val deeplink = deeplinkHandler.parseDeepLink(deeplinkString)!! + deeplinkManager.execute(context, deeplink) + deselectTariWalletAddress() + } + private fun showCantAddYourselfDialog() { showSimpleDialog( title = resourceManager.getString(R.string.contact_book_add_contact_cant_add_yourself_title), diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt index 197963b0e..9a343b417 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ContactBookFragment.kt @@ -16,7 +16,6 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayoutMediator import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentContactBookRootBinding import com.tari.android.wallet.extension.observe import com.tari.android.wallet.model.TariWalletAddress @@ -48,14 +47,11 @@ class ContactBookFragment : CommonFragment() @@ -160,7 +162,9 @@ class ShareViewModel : CommonViewModel() { } private fun onReceived(data: List) { - deeplinkViewModel.execute(DeepLink.Contacts(data)) + HomeActivity.instance.get()?.let { context -> + deeplinkManager.execute(context, DeepLink.Contacts(data), false) + } } companion object { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt index 1b0d897dc..13d0a1175 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt @@ -46,7 +46,7 @@ import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeeplinkHandler -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.security.SecurityPrefRepository import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository @@ -93,14 +93,15 @@ class HomeActivity : CommonActivity() { @Inject lateinit var deeplinkHandler: DeeplinkHandler + @Inject + lateinit var deeplinkManager: DeeplinkManager + @Inject lateinit var resourceManager: ResourceManager @Inject lateinit var tariSettingsRepository: TariSettingsPrefRepository - val deeplinkViewModel: DeeplinkViewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) appComponent.inject(this) @@ -122,12 +123,10 @@ class HomeActivity : CommonActivity() { val viewModel: HomeViewModel by viewModels() bindViewModel(viewModel) - subscribeToCommon(deeplinkViewModel) subscribeToCommon(viewModel.shareViewModel) subscribeToCommon(viewModel.shareViewModel.tariBluetoothServer) subscribeToCommon(viewModel.shareViewModel.tariBluetoothClient) - subscribeToCommon(viewModel.shareViewModel.deeplinkViewModel) viewModel.shareViewModel.tariBluetoothServer.init(this) viewModel.shareViewModel.tariBluetoothClient.init(this) @@ -284,9 +283,11 @@ class HomeActivity : CommonActivity() { fun willNotifyAboutNewTx(): Boolean = ui.viewPager.currentItem == INDEX_HOME private fun processIntentDeepLink(intent: Intent) { - intent.data?.toString()?.takeIf { it.isNotEmpty() }?.let { deeplinkString -> - deeplinkViewModel.tryToHandle(deeplinkString, isQrData = false) - } + intent.data?.toString()?.takeIf { it.isNotEmpty() } + ?.let { deeplinkString -> deeplinkHandler.parseDeepLink(deeplinkString) } + ?.let { deeplink -> + deeplinkManager.execute(context = this, deeplink = deeplink, isQrData = false) + } } override fun onDestroy() { @@ -333,7 +334,7 @@ class HomeActivity : CommonActivity() { private const val KEY_PAGE = "key_page" @Volatile - var instance: WeakReference = WeakReference(null) + var instance: WeakReference = WeakReference(null) // TODO I don't like this. Better not to share context globally private set } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt index 283514515..e512489fd 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerActivity.kt @@ -97,7 +97,6 @@ class QrScannerActivity : CommonActivity = _uiState.asStateFlow() @@ -44,18 +41,12 @@ class QrScannerViewModel(savedState: SavedStateHandle) : CommonViewModel() { private val qrScannerSource: QrScannerSource = savedState.get(EXTRA_QR_DATA_SOURCE) ?: QrScannerSource.None - fun onAlternativeApply() { + fun onAlternativeApply(context: Context) { backPressed.postValue(Unit) - executeWithDelay(deeplinkViewModel.viewModelScope) { - deeplinkViewModel.executeRawDeeplink(uiState.value.scannedDeeplink!!) - } - } - - private fun executeWithDelay(scope: CoroutineScope, action: () -> Unit) { - scope.launch(Dispatchers.IO) { + launchOnIo { delay(500) - scope.launch(Dispatchers.Main) { - action() + launchOnMain { + deeplinkManager.executeRawDeeplink(context, uiState.value.scannedDeeplink!!) } } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt index aea32d307..2f43fa93c 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt @@ -45,7 +45,6 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkViewModel import com.tari.android.wallet.databinding.FragmentHomeBinding import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.observe @@ -78,8 +77,6 @@ class HomeFragment : CommonFragment( // This listener is used only to animate the visibility of the scroll depth gradient view. private lateinit var balanceViewController: BalanceViewController - private val deeplinkViewModel: DeeplinkViewModel by viewModels() - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = FragmentHomeBinding.inflate(inflater, container, false).also { ui = it }.root @@ -89,8 +86,6 @@ class HomeFragment : CommonFragment( val viewModel: HomeFragmentViewModel by viewModels() bindViewModel(viewModel) - subscribeVM(deeplinkViewModel) - viewModel.checkPermission() setupUI() subscribeToViewModel() @@ -159,7 +154,7 @@ class HomeFragment : CommonFragment( override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == QrScannerActivity.REQUEST_QR_SCANNER && resultCode == Activity.RESULT_OK && data != null) { val qrDeepLink = data.parcelable(QrScannerActivity.EXTRA_DEEPLINK) ?: return - deeplinkViewModel.execute(qrDeepLink) + viewModel.handleDeeplink(requireContext(), qrDeepLink) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt index 5ff3be552..86e37b1f7 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt @@ -1,6 +1,7 @@ package com.tari.android.wallet.ui.fragment.tx +import android.content.Context import android.os.Build import android.text.SpannableString import android.text.Spanned @@ -12,6 +13,8 @@ import com.tari.android.wallet.R.string.error_no_connection_description import com.tari.android.wallet.R.string.error_no_connection_title import com.tari.android.wallet.R.string.error_node_unreachable_description import com.tari.android.wallet.R.string.error_node_unreachable_title +import com.tari.android.wallet.application.deeplinks.DeepLink +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager.StagedSecurityEffect import com.tari.android.wallet.application.walletManager.WalletManager.WalletEvent @@ -67,6 +70,9 @@ class HomeFragmentViewModel : CommonViewModel() { @Inject lateinit var stagedWalletSecurityManager: StagedWalletSecurityManager + @Inject + lateinit var deeplinkManager: DeeplinkManager + private val _balanceInfo = MutableLiveData() val balanceInfo: LiveData = _balanceInfo @@ -125,6 +131,10 @@ class HomeFragmentViewModel : CommonViewModel() { } } + fun handleDeeplink(context: Context, deepLink: DeepLink) { + deeplinkManager.execute(context, deepLink) + } + private fun updateList() { val list = transactionRepository.list.value ?: return txList.postValue(list.filterIsInstance().sortedByDescending { it.tx.timestamp }.take(TRANSACTION_AMOUNT_HOME_PAGE)) From 91cca8e07f78ce63f86d00a399a8cb05d60b030a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sat, 5 Oct 2024 17:04:54 +0200 Subject: [PATCH 12/32] Rename DeeplinkHandler to DeeplinkParser and encapsulate the whole deeplink logic to DeeplinkManager --- .../application/deeplinks/DeeplinkManager.kt | 5 +++++ .../{DeeplinkHandler.kt => DeeplinkParser.kt} | 12 +++--------- .../bluetooth/TariBluetoothClient.kt | 14 ++++++++------ .../bluetooth/TariBluetoothServer.kt | 10 +++++----- .../clipboardController/WalletAddressViewModel.kt | 6 +++--- .../contactSelection/ContactSelectionFragment.kt | 2 +- .../contactSelection/ContactSelectionViewModel.kt | 6 +----- .../contactBook/root/ContactBookViewModel.kt | 6 +++--- .../ui/fragment/contactBook/root/ShareViewModel.kt | 4 ---- .../wallet/ui/fragment/home/HomeActivity.kt | 6 +----- .../ui/fragment/profile/WalletInfoViewModel.kt | 6 +++--- .../wallet/ui/fragment/qr/QrScannerViewModel.kt | 6 +----- .../send/requestTari/RequestTariViewModel.kt | 6 +++--- .../changeBaseNode/ChangeBaseNodeViewModel.kt | 6 +++--- .../torBridges/TorBridgesSelectionViewModel.kt | 6 +++--- .../customBridges/CustomTorBridgesViewModel.kt | 6 +++--- 16 files changed, 46 insertions(+), 61 deletions(-) rename app/src/main/java/com/tari/android/wallet/application/deeplinks/{DeeplinkHandler.kt => DeeplinkParser.kt} (87%) diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index 4b49b3136..832a82b32 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -37,9 +37,14 @@ class DeeplinkManager @Inject constructor( private val dialogManager: DialogManager, private val walletManager: WalletManager, private val navigator: TariNavigator, + private val deeplinkParser: DeeplinkParser, @ApplicationScope private val applicationScope: CoroutineScope, ) { + fun parseDeepLink(deepLink: String): DeepLink? = deeplinkParser.parse(deepLink) + + fun getDeeplinkString(deeplink: DeepLink): String = deeplinkParser.toDeeplink(deeplink) + /** * Executes the given deeplink, but first shows a confirmation dialog. */ diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt similarity index 87% rename from app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt rename to app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt index 405f37cd0..a7e710867 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkHandler.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt @@ -9,16 +9,10 @@ import javax.inject.Inject import javax.inject.Singleton -// TODO DeepLinkParser?? -// TODO move to deeplinkManager ?? @Singleton -class DeeplinkHandler @Inject constructor(private val networkRepository: NetworkPrefRepository) { +class DeeplinkParser @Inject constructor(private val networkRepository: NetworkPrefRepository) { - fun parseDeepLink(deepLink: String): DeepLink? = parse(deepLink) - - fun getDeeplinkString(deeplink: DeepLink): String = toDeeplink(deeplink) - - private fun parse(deepLink: String): DeepLink? { + fun parse(deepLink: String): DeepLink? { val torBridges = getTorDeeplink(deepLink) if (torBridges.isNotEmpty()) { return DeepLink.TorBridges(torBridges) @@ -49,7 +43,7 @@ class DeeplinkHandler @Inject constructor(private val networkRepository: Network } } - private fun toDeeplink(deepLink: DeepLink): String { + fun toDeeplink(deepLink: DeepLink): String { if (deepLink is DeepLink.TorBridges) { return deepLink.torConfigurations.joinToString("\n") { "${it.ip}:${it.port} ${it.fingerprint}" diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt index 6d1c1c2f9..09f538c5a 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothClient.kt @@ -9,7 +9,7 @@ import android.os.Looper import android.os.ParcelUuid import androidx.lifecycle.viewModelScope import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.welie.blessed.BluetoothCentralManager import com.welie.blessed.BluetoothCentralManagerCallback import com.welie.blessed.BluetoothPeripheral @@ -24,7 +24,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class TariBluetoothClient @Inject constructor(val deeplinkHandler: DeeplinkHandler) : TariBluetoothAdapter() { +class TariBluetoothClient @Inject constructor(val deeplinkManager: DeeplinkManager) : TariBluetoothAdapter() { var onSuccessSharing: () -> Unit = {} var onFailedSharing: (String) -> Unit = {} @@ -34,9 +34,11 @@ class TariBluetoothClient @Inject constructor(val deeplinkHandler: DeeplinkHandl var foundDevice: BluetoothPeripheral? = null var myGatt: BluetoothGatt? = null - val manager: BluetoothCentralManager by lazy { BluetoothCentralManager(fragappCompatActivity!!, callback, Handler(Looper.getMainLooper())).apply { - disableLogging() - } } + val manager: BluetoothCentralManager by lazy { + BluetoothCentralManager(fragappCompatActivity!!, callback, Handler(Looper.getMainLooper())).apply { + disableLogging() + } + } val callback = object : BluetoothCentralManagerCallback() { @@ -137,7 +139,7 @@ class TariBluetoothClient @Inject constructor(val deeplinkHandler: DeeplinkHandl private fun doHandling(string: String): GattStatus { logger.i("contactlessPayment: handle: url: $string") - val handled = runCatching { deeplinkHandler.parseDeepLink(string) }.getOrNull() + val handled = runCatching { deeplinkManager.parseDeepLink(string) }.getOrNull() logger.i("contactlessPayment: handle: handled: $handled") diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt index 58a9168a0..9a5bdbcc1 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/bluetooth/TariBluetoothServer.kt @@ -8,9 +8,9 @@ import android.bluetooth.le.AdvertiseData import android.bluetooth.le.AdvertiseSettings import android.os.ParcelUuid import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler -import com.tari.android.wallet.data.sharedPrefs.bluetooth.BluetoothServerState +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.bluetooth.BluetoothPrefRepository +import com.tari.android.wallet.data.sharedPrefs.bluetooth.BluetoothServerState import com.tari.android.wallet.extension.addTo import com.tari.android.wallet.util.ContactUtil import com.welie.blessed.BluetoothCentral @@ -29,7 +29,7 @@ import javax.inject.Singleton @Singleton class TariBluetoothServer @Inject constructor( private val shareSettingsRepository: BluetoothPrefRepository, - private val deeplinkHandler: DeeplinkHandler, + private val deeplinkManager: DeeplinkManager, private val contactUtil: ContactUtil, ) : TariBluetoothAdapter() { @@ -120,7 +120,7 @@ class TariBluetoothServer @Inject constructor( private fun doHandling(string: String): GattStatus { logger.i("share: handle: url: $string") - val handled = runCatching { deeplinkHandler.parseDeepLink(string) }.getOrNull() + val handled = runCatching { deeplinkManager.parseDeepLink(string) }.getOrNull() logger.i("share: handle: handled: $handled") @@ -141,7 +141,7 @@ class TariBluetoothServer @Inject constructor( fun initiateReading() { if (shareChunkedData.isNotEmpty()) return val myWalletAddress = sharedPrefsRepository.walletAddress - val data = deeplinkHandler.getDeeplinkString( + val data = deeplinkManager.getDeeplinkString( DeepLink.UserProfile( tariAddress = sharedPrefsRepository.walletAddressBase58.orEmpty(), alias = contactUtil.normalizeAlias( diff --git a/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt index cacedc9de..cee92a4d9 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/component/clipboardController/WalletAddressViewModel.kt @@ -2,7 +2,7 @@ package com.tari.android.wallet.ui.component.clipboardController import android.content.ClipboardManager import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.model.TariWalletAddress @@ -16,7 +16,7 @@ class WalletAddressViewModel : CommonViewModel() { lateinit var clipboardManager: ClipboardManager @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager val discoveredWalletAddressFromClipboard = SingleLiveEvent() @@ -53,7 +53,7 @@ class WalletAddressViewModel : CommonViewModel() { } private fun findValidEmojiId(query: String): TariWalletAddress? { - return when (val deepLink = deeplinkHandler.parseDeepLink(query)) { + return when (val deepLink = deeplinkManager.parseDeepLink(query)) { is DeepLink.Send -> deepLink.walletAddress is DeepLink.UserProfile -> deepLink.tariAddress is DeepLink.Contacts -> deepLink.contacts.firstOrNull()?.tariAddress diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt index d3990de8d..b6810e0cc 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt @@ -338,7 +338,7 @@ open class ContactSelectionFragment : CommonFragment() { @Inject lateinit var networkRepository: NetworkPrefRepository - @Inject - lateinit var deeplinkHandler: DeeplinkHandler - @Inject lateinit var deeplinkManager: DeeplinkManager @@ -284,7 +280,7 @@ class HomeActivity : CommonActivity() { private fun processIntentDeepLink(intent: Intent) { intent.data?.toString()?.takeIf { it.isNotEmpty() } - ?.let { deeplinkString -> deeplinkHandler.parseDeepLink(deeplinkString) } + ?.let { deeplinkString -> deeplinkManager.parseDeepLink(deeplinkString) } ?.let { deeplink -> deeplinkManager.execute(context = this, deeplink = deeplink, isQrData = false) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt index 83d6a707a..451205aaf 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/profile/WalletInfoViewModel.kt @@ -5,7 +5,7 @@ import android.graphics.Bitmap import com.tari.android.wallet.R import com.tari.android.wallet.application.YatAdapter import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain @@ -38,7 +38,7 @@ class WalletInfoViewModel : CommonViewModel() { lateinit var yatAdapter: YatAdapter @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager @Inject lateinit var contactUtil: ContactUtil @@ -56,7 +56,7 @@ class WalletInfoViewModel : CommonViewModel() { ) val uiState = _uiState.asStateFlow() - private val shareProfileDeeplink = deeplinkHandler.getDeeplinkString( + private val shareProfileDeeplink = deeplinkManager.getDeeplinkString( DeepLink.UserProfile( tariAddress = corePrefRepository.walletAddressBase58.orEmpty(), alias = contactUtil.normalizeAlias(uiState.value.alias, corePrefRepository.walletAddress), diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerViewModel.kt index 4412a994c..bb076f7db 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/qr/QrScannerViewModel.kt @@ -4,7 +4,6 @@ import android.content.Context import androidx.lifecycle.SavedStateHandle import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.event.EffectChannelFlow import com.tari.android.wallet.extension.launchOnIo @@ -23,9 +22,6 @@ import javax.inject.Inject class QrScannerViewModel(savedState: SavedStateHandle) : CommonViewModel() { - @Inject - lateinit var deeplinkHandler: DeeplinkHandler - @Inject lateinit var deeplinkManager: DeeplinkManager @@ -59,7 +55,7 @@ class QrScannerViewModel(savedState: SavedStateHandle) : CommonViewModel() { } fun onScanResult(text: String?) { - val deeplink = deeplinkHandler.parseDeepLink(text.orEmpty()) + val deeplink = deeplinkManager.parseDeepLink(text.orEmpty()) if (deeplink == null) { _uiState.update { it.copy(scanError = true) } } else { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/requestTari/RequestTariViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/requestTari/RequestTariViewModel.kt index 92851b707..1edcd490a 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/requestTari/RequestTariViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/requestTari/RequestTariViewModel.kt @@ -1,7 +1,7 @@ package com.tari.android.wallet.ui.fragment.send.requestTari import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.ui.common.CommonViewModel @@ -13,11 +13,11 @@ class RequestTariViewModel : CommonViewModel() { lateinit var sharedPrefsWrapper: CorePrefRepository @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager init { component.inject(this) } - fun getDeepLink(amount: MicroTari): String = deeplinkHandler.getDeeplinkString(DeepLink.Send(sharedPrefsWrapper.walletAddressBase58!!, amount)) + fun getDeepLink(amount: MicroTari): String = deeplinkManager.getDeeplinkString(DeepLink.Send(sharedPrefsWrapper.walletAddressBase58!!, amount)) } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt index 6a48bf948..df485b703 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/baseNodeConfig/changeBaseNode/ChangeBaseNodeViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler @@ -34,7 +34,7 @@ class ChangeBaseNodeViewModel : CommonViewModel() { lateinit var baseNodeStateHandler: BaseNodeStateHandler @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager private val _baseNodeList = MutableLiveData>() val baseNodeList: LiveData> = _baseNodeList @@ -57,7 +57,7 @@ class ChangeBaseNodeViewModel : CommonViewModel() { } fun showQrCode(baseNodeDto: BaseNodeDto) { - val data = deeplinkHandler.getDeeplinkString(baseNodeDto.toDeeplink()) + val data = deeplinkManager.getDeeplinkString(baseNodeDto.toDeeplink()) showModularDialog( ModularDialogArgs( DialogArgs(true, canceledOnTouchOutside = true), listOf( diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt index c62ba8d7b..680d43746 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/TorBridgesSelectionViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfigurationList import com.tari.android.wallet.data.sharedPrefs.tor.TorPrefRepository import com.tari.android.wallet.extension.collectFlow @@ -38,7 +38,7 @@ class TorBridgesSelectionViewModel : CommonViewModel() { lateinit var torProxyManager: TorProxyManager @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager @Inject lateinit var torProxyStateHandler: TorProxyStateHandler @@ -98,7 +98,7 @@ class TorBridgesSelectionViewModel : CommonViewModel() { fun showBridgeQrCode(torBridgeItem: TorBridgeViewHolderItem) { if (torBridgeItem !is TorBridgeViewHolderItem.Bridge) return - val data = deeplinkHandler.getDeeplinkString(DeepLink.TorBridges(listOf(torBridgeItem.bridgeConfiguration))) + val data = deeplinkManager.getDeeplinkString(DeepLink.TorBridges(listOf(torBridgeItem.bridgeConfiguration))) showModularDialog( ModularDialogArgs( DialogArgs(true, canceledOnTouchOutside = true), listOf( diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt index 9a2bd269a..fea386a64 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/torBridges/customBridges/CustomTorBridgesViewModel.kt @@ -2,7 +2,7 @@ package com.tari.android.wallet.ui.fragment.settings.torBridges.customBridges import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink -import com.tari.android.wallet.application.deeplinks.DeeplinkHandler +import com.tari.android.wallet.application.deeplinks.DeeplinkManager import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfiguration import com.tari.android.wallet.data.sharedPrefs.tor.TorPrefRepository import com.tari.android.wallet.ui.common.CommonViewModel @@ -16,7 +16,7 @@ class CustomTorBridgesViewModel : CommonViewModel() { lateinit var torSharedRepository: TorPrefRepository @Inject - lateinit var deeplinkHandler: DeeplinkHandler + lateinit var deeplinkManager: DeeplinkManager var text = SingleLiveEvent() @@ -67,7 +67,7 @@ class CustomTorBridgesViewModel : CommonViewModel() { fun handleQrCode(deeplink: DeepLink) { if (deeplink is DeepLink.TorBridges) { - val text = deeplinkHandler.getDeeplinkString(deeplink) + val text = deeplinkManager.getDeeplinkString(deeplink) this.text.postValue(text) } } From 305f9a0b9e524c6a42dc92b38ebebce828cc5a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sun, 6 Oct 2024 11:12:09 +0200 Subject: [PATCH 13/32] Add the restore from paper wallet option for restoring a wallet --- .../model/recovery/WalletRestorationResult.kt | 29 ++++++++++--------- .../ui/fragment/home/navigation/Navigation.kt | 2 +- .../fragment/home/navigation/TariNavigator.kt | 4 +-- .../ChooseRestoreOptionViewModel.kt | 21 +++++++++++--- .../inputSeedWords/InputSeedWordsFragment.kt | 6 +++- .../inputSeedWords/InputSeedWordsViewModel.kt | 29 ++++++++++++++----- .../fragment_wallet_input_seed_words.xml | 22 +++++--------- 7 files changed, 69 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt b/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt index 5cbb24b6d..a5f33e302 100644 --- a/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt +++ b/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt @@ -16,33 +16,36 @@ import java.nio.ByteBuffer // attempt will be made // - If a unrecoverable error occurs the `RecoveryFailed` event will be returned and the client will need to start // a new process. +// TODO needs a refactor sealed class WalletRestorationResult { - class ConnectingToBaseNode : WalletRestorationResult() - class ConnectedToBaseNode : WalletRestorationResult() - class ConnectionToBaseNodeFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { + data object ConnectingToBaseNode : WalletRestorationResult() + data object ConnectedToBaseNode : WalletRestorationResult() + data class ConnectionToBaseNodeFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { override fun toString(): String = "${this.javaClass.simpleName} $retryCount / $retryLimit" } - class Progress(val currentBlock: Long, val numberOfBlocks: Long) : WalletRestorationResult() - class Completed(val numberOfUTXO: Long, val microTari: ByteArray) : WalletRestorationResult() - class ScanningRoundFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { + + data class Progress(val currentBlock: Long, val numberOfBlocks: Long) : WalletRestorationResult() + data class Completed(val numberOfUTXO: Long, val microTari: ByteArray) : WalletRestorationResult() + data class ScanningRoundFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { override fun toString(): String = "${this.javaClass.simpleName} $retryCount / $retryLimit" } - class RecoveryFailed : WalletRestorationResult() + + data object RecoveryFailed : WalletRestorationResult() companion object { - fun create(event: Int, firstArg: ByteArray, secondArgs: ByteArray) : WalletRestorationResult { + fun create(event: Int, firstArg: ByteArray, secondArgs: ByteArray): WalletRestorationResult { val first = bytesToLong(firstArg) val second = bytesToLong(secondArgs) Logger.t("WalletRestorationResult $event $first $second") - return when(event) { - 0 -> ConnectingToBaseNode() - 1 -> ConnectedToBaseNode() + return when (event) { + 0 -> ConnectingToBaseNode + 1 -> ConnectedToBaseNode 2 -> ConnectionToBaseNodeFailed(first, second) 3 -> Progress(first, second) 4 -> Completed(first, secondArgs) 5 -> ScanningRoundFailed(first, second) - 6 -> RecoveryFailed() - else -> TODO() + 6 -> RecoveryFailed + else -> TODO() // WHY?? } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt index 1fb738144..4343f4ed0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt @@ -98,7 +98,7 @@ sealed class Navigation { sealed class ChooseRestoreOptionNavigation : Navigation() { object ToEnterRestorePassword : ChooseRestoreOptionNavigation() - object ToRestoreWithRecoveryPhrase : ChooseRestoreOptionNavigation() + data class ToRestoreWithRecoveryPhrase(val seedWords: List?) : ChooseRestoreOptionNavigation() object OnRestoreCompleted : ChooseRestoreOptionNavigation() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 5a89ea448..41420215b 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -127,7 +127,7 @@ class TariNavigator @Inject constructor( is ContactBookNavigation.ToSelectTariUser -> addFragment(SelectUserContactFragment.newInstance()) is ChooseRestoreOptionNavigation.ToEnterRestorePassword -> toEnterRestorePassword() is ChooseRestoreOptionNavigation.OnRestoreCompleted -> onRestoreCompleted() - is ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase -> toRestoreWithRecoveryPhrase() + is ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase -> toRestoreWithRecoveryPhrase(navigation.seedWords) is AllSettingsNavigation.ToBugReporting -> DebugActivity.launch(activity, DebugNavigation.BugReport) is AllSettingsNavigation.ToMyProfile -> toMyProfile() is AllSettingsNavigation.ToAbout -> toAbout() @@ -188,7 +188,7 @@ class TariNavigator @Inject constructor( private fun toEnterRestorePassword() = addFragment(EnterRestorationPasswordFragment.newInstance()) - private fun toRestoreWithRecoveryPhrase() = addFragment(InputSeedWordsFragment.newInstance()) + private fun toRestoreWithRecoveryPhrase(seedWords: List?) = addFragment(InputSeedWordsFragment.createFragment(seedWords)) private fun toRestoreFromSeedWordsInProgress() = addFragment(WalletRestoringFromSeedWordsFragment.newInstance()) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index a63629593..bee06e323 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -23,7 +23,13 @@ import com.tari.android.wallet.model.throwIf import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.SingleLiveEvent +import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle +import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation +import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity +import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions import com.tari.android.wallet.util.WalletUtil @@ -89,12 +95,11 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } fun onRecoveryPhraseClicked() { - tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase) + tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase(seedWords = null)) } fun onPaperWalletClicked(fragment: Fragment) { - showNotReadyYetDialog() -// QrScannerActivity.startScanner(fragment, QrScannerSource.PaperWallet) + QrScannerActivity.startScanner(fragment, QrScannerSource.PaperWallet) } fun handleDeeplink(qrDeepLink: DeepLink) { @@ -182,7 +187,15 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } private fun showPaperWalletDialog(seedWords: List) { - // TODO + showModularDialog( + HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_title)), + BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_body)), + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_restore_button), ButtonStyle.Normal) { + hideDialog() + tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase(seedWords)) + }, + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_do_not_restore_button), ButtonStyle.Close), + ) } private fun showBackupFileNotFoundDialog() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt index 805df0f29..e8e0a3bf0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt @@ -212,6 +212,10 @@ class InputSeedWordsFragment : CommonFragment?): InputSeedWordsFragment = InputSeedWordsFragment().apply { + arguments = Bundle().apply { putStringArray(PARAMETER_SEED_WORDS, seedWords?.toTypedArray()) } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt index efd3eabd7..5b1d0c1d5 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt @@ -3,6 +3,7 @@ package com.tari.android.wallet.ui.fragment.restore.inputSeedWords import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.map import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager @@ -25,6 +26,7 @@ import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.dialog.modular.modules.input.InputModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation +import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsFragment.Companion.PARAMETER_SEED_WORDS import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.suggestions.SuggestionState import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.suggestions.SuggestionViewHolderItem import io.reactivex.disposables.CompositeDisposable @@ -34,7 +36,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import javax.inject.Inject -class InputSeedWordsViewModel : CommonViewModel() { +class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() { private var mnemonicList = mutableListOf() @@ -94,6 +96,16 @@ class InputSeedWordsViewModel : CommonViewModel() { _suggestions.addSource(focusedIndex) { processSuggestions() } _suggestions.addSource(_words) { processSuggestions() } + + // Check if there are seed words for paper wallet restoration + val seedWords = savedState.get>(PARAMETER_SEED_WORDS) + if (seedWords != null) { + for (index in seedWords.indices) { + addWord(index, seedWords[index]) + } + finishEntering() + startRestoringWallet() + } } fun startRestoringWallet() { @@ -102,8 +114,7 @@ class InputSeedWordsViewModel : CommonViewModel() { val result = seedPhrase.init(words) if (result == SeedPhrase.SeedPhraseCreationResult.Success) { - seedPhraseRepository.save(seedPhrase) - startRestoring() + startRestoring(seedPhrase) } else { handleSeedPhraseResult(result) } @@ -119,9 +130,11 @@ class InputSeedWordsViewModel : CommonViewModel() { } } - private fun startRestoring() { + private fun startRestoring(seedPhrase: SeedPhrase) { _inProgress.postValue(true) + seedPhraseRepository.save(seedPhrase) + launchOnIo { walletManager.doOnWalletFailed { exception -> if (WalletError(exception) == WalletError.NoError) { @@ -136,7 +149,7 @@ class InputSeedWordsViewModel : CommonViewModel() { launchOnIo { walletManager.doOnWalletRunning { - navigation.postValue(Navigation.InputSeedWordsNavigation.ToRestoreFormSeedWordsInProgress) + tariNavigator.navigate(Navigation.InputSeedWordsNavigation.ToRestoreFormSeedWordsInProgress) _inProgress.postValue(false) } } @@ -152,10 +165,10 @@ class InputSeedWordsViewModel : CommonViewModel() { private fun handleSeedPhraseResult(result: SeedPhrase.SeedPhraseCreationResult) { val errorDialogArgs = when (result) { is SeedPhrase.SeedPhraseCreationResult.Failed -> RestorationError.Unknown(resourceManager) - SeedPhrase.SeedPhraseCreationResult.InvalidSeedPhrase, - SeedPhrase.SeedPhraseCreationResult.InvalidSeedWord -> RestorationError.Invalid(resourceManager) + is SeedPhrase.SeedPhraseCreationResult.InvalidSeedPhrase, + is SeedPhrase.SeedPhraseCreationResult.InvalidSeedWord -> RestorationError.Invalid(resourceManager) - SeedPhrase.SeedPhraseCreationResult.SeedPhraseNotCompleted -> RestorationError.SeedPhraseTooShort(resourceManager) + is SeedPhrase.SeedPhraseCreationResult.SeedPhraseNotCompleted -> RestorationError.SeedPhraseTooShort(resourceManager) else -> RestorationError.Unknown(resourceManager) } onError(errorDialogArgs) diff --git a/app/src/main/res/layout/fragment_wallet_input_seed_words.xml b/app/src/main/res/layout/fragment_wallet_input_seed_words.xml index 30a783959..bcea3b774 100644 --- a/app/src/main/res/layout/fragment_wallet_input_seed_words.xml +++ b/app/src/main/res/layout/fragment_wallet_input_seed_words.xml @@ -1,6 +1,5 @@ - - @@ -21,7 +20,7 @@ android:layout_height="wrap_content" app:text="@string/restore_from_seed_words_title" /> - @@ -88,8 +87,8 @@ android:layout_width="match_parent" android:layout_height="@dimen/common_action_button_height" android:layout_marginHorizontal="25dp" - android:visibility="gone" android:layout_marginTop="60dp" + android:visibility="gone" app:title="@string/restore_from_seed_words_select_node" /> - - - - - + - - - - + - - \ No newline at end of file + \ No newline at end of file From 9813f60de235c87a27372d458d43839de4c6a6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sun, 6 Oct 2024 13:53:19 +0200 Subject: [PATCH 14/32] Add WalletRestorationStateHandler, improve restoration state logging --- .../walletManager/WalletManager.kt | 17 +++++- .../android/wallet/di/ApplicationComponent.kt | 3 + .../com/tari/android/wallet/event/EventBus.kt | 5 -- .../com/tari/android/wallet/ffi/FFIBase.kt | 9 --- .../tari/android/wallet/ffi/FFICommsConfig.kt | 4 ++ .../com/tari/android/wallet/ffi/FFIWallet.kt | 24 ++++++-- .../android/wallet/ffi/FFIWalletListener.kt | 4 +- .../model/recovery/WalletRestorationResult.kt | 57 ----------------- .../NotificationBroadcastReceiver.kt | 19 ++++-- .../wallet/recovery/WalletRestorationState.kt | 51 ++++++++++++++++ .../recovery/WalletRestorationStateHandler.kt | 18 ++++++ .../WalletRestoringFromSeedWordsViewModel.kt | 61 +++++++++---------- 12 files changed, 155 insertions(+), 117 deletions(-) delete mode 100644 app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt create mode 100644 app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationState.kt create mode 100644 app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationStateHandler.kt diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index d0b748927..f8173f810 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -71,8 +71,9 @@ import com.tari.android.wallet.model.TransactionSendStatus import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.fullBase58 -import com.tari.android.wallet.model.recovery.WalletRestorationResult import com.tari.android.wallet.notification.NotificationHelper +import com.tari.android.wallet.recovery.WalletRestorationState +import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeSyncState @@ -127,6 +128,7 @@ class WalletManager @Inject constructor( private val app: TariWalletApplication, private val notificationHelper: NotificationHelper, private val notificationService: NotificationService, + private val walletRestorationStateHandler: WalletRestorationStateHandler, @ApplicationScope private val applicationScope: CoroutineScope, ) : OutboundTxNotifier { @@ -245,6 +247,15 @@ class WalletManager @Inject constructor( } } + /** + * Starts the wallet recovery process. Returns true if the recovery process was started successfully. + * The recovery process events will be handled in the onWalletRestoration() callback. + */ + fun startRecovery(baseNode: BaseNodeDto, recoveryOutputMessage: String): Boolean { + val baseNodeFFI = FFIPublicKey(HexString(baseNode.publicKeyHex)) + return walletInstance?.startRecovery(baseNodeFFI, recoveryOutputMessage) ?: false + } + private fun startWallet() { if (walletState.value is WalletState.NotReady || walletState.value is WalletState.Failed) { logger.i("Initialize wallet started") @@ -481,8 +492,8 @@ class WalletManager @Inject constructor( } } - override fun onWalletRestoration(result: WalletRestorationResult) = runOnMain { - EventBus.walletRestorationState.post(result) // TODO replace with flow!!! + override fun onWalletRestoration(state: WalletRestorationState) = runOnMain { + walletRestorationStateHandler.updateState(state) } override fun onWalletScannedHeight(height: Int) = runOnMain { diff --git a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt index abb73e4e1..60ad6c4ea 100644 --- a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt +++ b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt @@ -35,6 +35,7 @@ package com.tari.android.wallet.di import android.content.ClipboardManager import com.tari.android.wallet.application.TariWalletApplication import com.tari.android.wallet.application.securityStage.StagedWalletSecurityManager +import com.tari.android.wallet.notification.NotificationBroadcastReceiver import com.tari.android.wallet.service.service.WalletService import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.component.clipboardController.WalletAddressViewModel @@ -201,5 +202,7 @@ interface ApplicationComponent { fun inject(viewModel: EnterPinCodeViewModel) fun inject(viewModel: ChangeBiometricsViewModel) + fun inject(notificationBroadcastReceiver: NotificationBroadcastReceiver) + fun getClipboardManager(): ClipboardManager } diff --git a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt index dba43fe6f..c79a3b024 100644 --- a/app/src/main/java/com/tari/android/wallet/event/EventBus.kt +++ b/app/src/main/java/com/tari/android/wallet/event/EventBus.kt @@ -34,7 +34,6 @@ package com.tari.android.wallet.event import com.tari.android.wallet.infrastructure.backup.BackupsState import com.tari.android.wallet.model.BalanceInfo -import com.tari.android.wallet.model.recovery.WalletRestorationResult /** * Event bus for the pub/sub model. @@ -51,17 +50,13 @@ object EventBus : GeneralEventBus() { val backupState = BehaviorEventBus() - val walletRestorationState = BehaviorEventBus() - fun unsubscribeAll(subscriber: Any) { EventBus.unsubscribe(subscriber) backupState.unsubscribe(subscriber) - walletRestorationState.unsubscribe(subscriber) } override fun clear() { super.clear() backupState.clear() - walletRestorationState.clear() } } diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIBase.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIBase.kt index 46cf231d8..50bcca9a9 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIBase.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIBase.kt @@ -32,9 +32,6 @@ */ package com.tari.android.wallet.ffi -import com.orhanobut.logger.Logger -import com.orhanobut.logger.Printer - typealias FFIPointer = Long const val nullptr = 0L @@ -50,12 +47,6 @@ abstract class FFIBase { var pointer = nullptr protected set - companion object { - @JvmStatic - protected val logger: Printer - get() = Logger.t(this::class.simpleName) - } - abstract fun destroy() protected fun finalize() { diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFICommsConfig.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFICommsConfig.kt index cb09bc72d..b3ff42612 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFICommsConfig.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFICommsConfig.kt @@ -32,6 +32,7 @@ */ package com.tari.android.wallet.ffi +import com.orhanobut.logger.Logger import java.io.File /** @@ -41,6 +42,9 @@ import java.io.File */ class FFICommsConfig() : FFIBase() { + private val logger + get() = Logger.t(FFICommsConfig::class.simpleName) + private external fun jniCreate( publicAddress: String, transport: FFITariTransportConfig, diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt index 3d1706731..c9d655ec1 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWallet.kt @@ -32,6 +32,7 @@ */ package com.tari.android.wallet.ffi +import com.orhanobut.logger.Logger import com.tari.android.wallet.BuildConfig import com.tari.android.wallet.data.sharedPrefs.network.TariNetwork import com.tari.android.wallet.model.BalanceInfo @@ -45,7 +46,7 @@ import com.tari.android.wallet.model.TariUnblindedOutput import com.tari.android.wallet.model.TariVector import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.model.Tx -import com.tari.android.wallet.model.recovery.WalletRestorationResult +import com.tari.android.wallet.recovery.WalletRestorationState import java.math.BigInteger /** @@ -58,6 +59,9 @@ class FFIWallet( private val listener: FFIWalletListener, ) : FFIBase() { + private val logger + get() = Logger.t(FFIWallet::class.simpleName) + companion object { // values for the wallet initialization private val LOG_VERBOSITY: Int = if (BuildConfig.BUILD_TYPE == "debug") 11 else 4 @@ -364,9 +368,21 @@ class FFIWallet( } private fun onWalletRecovery(event: Int, firstArg: ByteArray, secondArg: ByteArray) { - val result = WalletRestorationResult.create(event, firstArg, secondArg) - logger.i("Wallet restored with $result") - listener.onWalletRestoration(result) + val state = WalletRestorationState.create(event, firstArg, secondArg) + logger.i( + "Wallet restoration: ${ + when (state) { + is WalletRestorationState.ConnectingToBaseNode -> "Connecting to base node" + is WalletRestorationState.ConnectedToBaseNode -> "Connected to base node" + is WalletRestorationState.ConnectionToBaseNodeFailed -> "Connection to base node failed: ${state.retryCount}/${state.retryLimit}" + is WalletRestorationState.Progress -> "Progress: ${state.currentBlock}/${state.numberOfBlocks}" + is WalletRestorationState.Completed -> "Completed: ${state.numberOfUTXO} UTXOs, ${state.microTari.size} MicroTari" + is WalletRestorationState.ScanningRoundFailed -> "Scanning round failed: ${state.retryCount}/${state.retryLimit}" + is WalletRestorationState.RecoveryFailed -> "Recovery failed" + } + }" + ) + listener.onWalletRestoration(state) } override fun destroy() { diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt index 516da8913..888192554 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFIWalletListener.kt @@ -33,7 +33,7 @@ package com.tari.android.wallet.ffi import com.tari.android.wallet.model.* -import com.tari.android.wallet.model.recovery.WalletRestorationResult +import com.tari.android.wallet.recovery.WalletRestorationState import java.math.BigInteger /** @@ -56,7 +56,7 @@ interface FFIWalletListener { fun onTxValidationComplete(responseId: BigInteger, status: TransactionValidationStatus) fun onBalanceUpdated(balanceInfo: BalanceInfo) fun onConnectivityStatus(status: Int) - fun onWalletRestoration(result: WalletRestorationResult) + fun onWalletRestoration(state: WalletRestorationState) fun onWalletScannedHeight(height: Int) fun onBaseNodeStateChanged(baseNodeState: FFITariBaseNodeState) } diff --git a/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt b/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt deleted file mode 100644 index a5f33e302..000000000 --- a/app/src/main/java/com/tari/android/wallet/model/recovery/WalletRestorationResult.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tari.android.wallet.model.recovery - -import com.orhanobut.logger.Logger -import java.nio.ByteBuffer - -// If connection to a base node is successful the flow of callbacks should be: -// - The process will start with a callback with `ConnectingToBaseNode` showing a connection is being attempted -// this could be repeated multiple times until a connection is made. -// - The next a callback with `ConnectedToBaseNode` indicate a successful base node connection and process has -// started -// - In Progress callbacks will be of the form (n, m) where n < m -// - If the process completed successfully then the final `Completed` callback will return how many UTXO's were -// scanned and how much MicroTari was recovered -// - If there is an error in the connection process then the `ConnectionToBaseNodeFailed` will be returned -// - If there is a minor error in scanning then `ScanningRoundFailed` will be returned and another connection/sync -// attempt will be made -// - If a unrecoverable error occurs the `RecoveryFailed` event will be returned and the client will need to start -// a new process. -// TODO needs a refactor -sealed class WalletRestorationResult { - data object ConnectingToBaseNode : WalletRestorationResult() - data object ConnectedToBaseNode : WalletRestorationResult() - data class ConnectionToBaseNodeFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { - override fun toString(): String = "${this.javaClass.simpleName} $retryCount / $retryLimit" - } - - data class Progress(val currentBlock: Long, val numberOfBlocks: Long) : WalletRestorationResult() - data class Completed(val numberOfUTXO: Long, val microTari: ByteArray) : WalletRestorationResult() - data class ScanningRoundFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationResult() { - override fun toString(): String = "${this.javaClass.simpleName} $retryCount / $retryLimit" - } - - data object RecoveryFailed : WalletRestorationResult() - - companion object { - fun create(event: Int, firstArg: ByteArray, secondArgs: ByteArray): WalletRestorationResult { - val first = bytesToLong(firstArg) - val second = bytesToLong(secondArgs) - Logger.t("WalletRestorationResult $event $first $second") - return when (event) { - 0 -> ConnectingToBaseNode - 1 -> ConnectedToBaseNode - 2 -> ConnectionToBaseNodeFailed(first, second) - 3 -> Progress(first, second) - 4 -> Completed(first, secondArgs) - 5 -> ScanningRoundFailed(first, second) - 6 -> RecoveryFailed - else -> TODO() // WHY?? - } - } - - private fun bytesToLong(bytes: ByteArray): Long = ByteBuffer.allocate(java.lang.Long.BYTES).apply { - put(bytes) - flip() - }.long - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/notification/NotificationBroadcastReceiver.kt b/app/src/main/java/com/tari/android/wallet/notification/NotificationBroadcastReceiver.kt index 6d5f7cac8..80d10904b 100644 --- a/app/src/main/java/com/tari/android/wallet/notification/NotificationBroadcastReceiver.kt +++ b/app/src/main/java/com/tari/android/wallet/notification/NotificationBroadcastReceiver.kt @@ -36,21 +36,30 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.orhanobut.logger.Logger -import com.tari.android.wallet.event.EventBus -import com.tari.android.wallet.model.recovery.WalletRestorationResult -import com.tari.android.wallet.ui.fragment.splash.SplashActivity +import com.tari.android.wallet.di.DiContainer +import com.tari.android.wallet.recovery.WalletRestorationState +import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.ui.fragment.restore.activity.WalletRestoreActivity +import com.tari.android.wallet.ui.fragment.splash.SplashActivity +import javax.inject.Inject class NotificationBroadcastReceiver : BroadcastReceiver() { + @Inject + lateinit var walletRestorationStateHandler: WalletRestorationStateHandler + private val logger get() = Logger.t(NotificationBroadcastReceiver::class.simpleName) + init { + DiContainer.appComponent.inject(this) + } + override fun onReceive(context: Context, intent: Intent) { logger.d("NotificationBroadcastReceiver received") - val restorationState = EventBus.walletRestorationState.publishSubject.value - val newIntent: Intent = if (restorationState != null && restorationState !is WalletRestorationResult.Completed) { + val restorationState = walletRestorationStateHandler.walletRestorationState.value + val newIntent: Intent = if (restorationState !is WalletRestorationState.Completed) { Intent(context, WalletRestoreActivity::class.java) } else { if (HomeActivity.instance.get() != null) { diff --git a/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationState.kt b/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationState.kt new file mode 100644 index 000000000..d02aed3a3 --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationState.kt @@ -0,0 +1,51 @@ +package com.tari.android.wallet.recovery + +import com.orhanobut.logger.Logger +import java.nio.ByteBuffer + +/** + * Represents the various states of the wallet restoration process. + * + * The flow of callbacks for a successful connection to a base node is as follows: + * + * - **ConnectingToBaseNode**: The process starts with a callback indicating that a connection attempt is in progress. + * This callback may be repeated multiple times until a connection is established. + * - **ConnectedToBaseNode**: Once a connection is successfully made, this callback is triggered, indicating that the process has started. + * - **Progress**: Progress callbacks will be in the form of (n, m) where n < m, showing the progress of the process. + * - **Completed**: If the process completes successfully, this callback will be returned, indicating the number of UTXOs scanned and the amount of MicroTari recovered. + * - **ConnectionToBaseNodeFailed**: If there is an error during the connection process, this callback will be returned. + * - **ScanningRoundFailed**: If there is a minor error during scanning, this callback will be returned, and another connection/sync attempt will be made. + * - **RecoveryFailed**: If an unrecoverable error occurs, this event will be returned, and the client will need to start a new process. + */ +sealed class WalletRestorationState { + data object ConnectingToBaseNode : WalletRestorationState() + data object ConnectedToBaseNode : WalletRestorationState() + data class ConnectionToBaseNodeFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationState() + data class Progress(val currentBlock: Long, val numberOfBlocks: Long) : WalletRestorationState() + data class Completed(val numberOfUTXO: Long, val microTari: ByteArray) : WalletRestorationState() + data class ScanningRoundFailed(val retryCount: Long, val retryLimit: Long) : WalletRestorationState() + data object RecoveryFailed : WalletRestorationState() + + companion object { + fun create(event: Int, firstArg: ByteArray, secondArgs: ByteArray): WalletRestorationState { + val first = bytesToLong(firstArg) + val second = bytesToLong(secondArgs) + Logger.t("WalletRestorationResult $event $first $second") + return when (event) { + 0 -> ConnectingToBaseNode + 1 -> ConnectedToBaseNode + 2 -> ConnectionToBaseNodeFailed(first, second) + 3 -> Progress(first, second) + 4 -> Completed(first, secondArgs) + 5 -> ScanningRoundFailed(first, second) + 6 -> RecoveryFailed + else -> error("Invalid event type: $event") + } + } + + private fun bytesToLong(bytes: ByteArray): Long = ByteBuffer.allocate(java.lang.Long.BYTES).apply { + put(bytes) + flip() + }.long + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationStateHandler.kt b/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationStateHandler.kt new file mode 100644 index 000000000..151cab568 --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/recovery/WalletRestorationStateHandler.kt @@ -0,0 +1,18 @@ +package com.tari.android.wallet.recovery + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class WalletRestorationStateHandler @Inject constructor() { + + private val _walletRestorationState = MutableStateFlow(WalletRestorationState.ConnectingToBaseNode) + val walletRestorationState = _walletRestorationState.asStateFlow() + + fun updateState(newState: WalletRestorationState) { + _walletRestorationState.update { newState } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt index 564f9ff4c..253d090ca 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt @@ -2,23 +2,19 @@ package com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto -import com.tari.android.wallet.event.EventBus -import com.tari.android.wallet.extension.addTo -import com.tari.android.wallet.ffi.FFIPublicKey -import com.tari.android.wallet.ffi.HexString -import com.tari.android.wallet.model.recovery.WalletRestorationResult +import com.tari.android.wallet.extension.collectFlow +import com.tari.android.wallet.extension.launchOnIo +import com.tari.android.wallet.recovery.WalletRestorationState +import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import io.reactivex.disposables.CompositeDisposable -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.text.DecimalFormat import javax.inject.Inject @@ -30,6 +26,9 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { @Inject lateinit var baseNodesManager: BaseNodesManager + @Inject + lateinit var walletRestorationStateHandler: WalletRestorationStateHandler + private lateinit var baseNodeIterator: Iterator private val _recoveryState = MutableLiveData(RecoveryState.ConnectingToBaseNode(resourceManager)) @@ -39,12 +38,12 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { component.inject(this) } - fun startRestoring() = viewModelScope.launch(Dispatchers.IO) { + fun startRestoring() = launchOnIo { baseNodeIterator = baseNodesManager.baseNodeList.iterator() tryNextBaseNode() } - private fun tryNextBaseNode() = viewModelScope.launch(Dispatchers.IO) { + private fun tryNextBaseNode() = launchOnIo { logger.i("set next base node ${baseNodeIterator.hasNext()}") if (baseNodeIterator.hasNext()) { startRestoringOnNode(baseNodeIterator.next()) @@ -55,9 +54,8 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private fun startRestoringOnNode(baseNode: BaseNodeDto) { try { - val baseNodeFFI = FFIPublicKey(HexString(baseNode.publicKeyHex)) - val result = walletManager.walletInstance?.startRecovery(baseNodeFFI, resourceManager.getString(R.string.restore_wallet_output_message)) - if (result == true) { + val result = walletManager.startRecovery(baseNode, resourceManager.getString(R.string.restore_wallet_output_message)) + if (result) { subscribeOnRestorationState() return } else { @@ -70,26 +68,23 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { } private fun subscribeOnRestorationState() { - EventBus.walletRestorationState.publishSubject.subscribe { - logger.i(it.toString()) - when (it) { - is WalletRestorationResult.ConnectingToBaseNode -> onProgress(RecoveryState.ConnectingToBaseNode(resourceManager)) - is WalletRestorationResult.ConnectedToBaseNode -> onProgress(RecoveryState.ConnectedToBaseNode(resourceManager)) - is WalletRestorationResult.ScanningRoundFailed -> onConnectionFailed(it.retryCount, it.retryLimit) - is WalletRestorationResult.ConnectionToBaseNodeFailed -> onConnectionFailed(it.retryCount, it.retryLimit) - is WalletRestorationResult.Progress -> onProgress(RecoveryState.Recovery(resourceManager, it.currentBlock, it.numberOfBlocks)) - is WalletRestorationResult.RecoveryFailed -> { - logger.i("recovery failed ${baseNodeIterator.hasNext()}") - if (!baseNodeIterator.hasNext()) { - onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed)) - } - } - - is WalletRestorationResult.Completed -> onSuccessRestoration() + collectFlow(walletRestorationStateHandler.walletRestorationState) { state -> + when (state) { + is WalletRestorationState.ConnectingToBaseNode -> onProgress(RecoveryState.ConnectingToBaseNode(resourceManager)) + is WalletRestorationState.ConnectedToBaseNode -> onProgress(RecoveryState.ConnectedToBaseNode(resourceManager)) + is WalletRestorationState.ScanningRoundFailed -> onConnectionFailed(state.retryCount, state.retryLimit) + is WalletRestorationState.ConnectionToBaseNodeFailed -> onConnectionFailed(state.retryCount, state.retryLimit) + is WalletRestorationState.Progress -> onProgress(RecoveryState.Recovery(resourceManager, state.currentBlock, state.numberOfBlocks)) + is WalletRestorationState.RecoveryFailed -> onError( + RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed) + ) + + is WalletRestorationState.Completed -> onSuccessRestoration() } - }.addTo(compositeDisposable) + } } + private fun onConnectionFailed(retryCount: Long, retryLimit: Long) { if (retryCount == retryLimit) { compositeDisposable.dispose() @@ -101,8 +96,10 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { } private fun onError(restorationError: RestorationError) { - walletServiceLauncher.stopAndDelete() - showModularDialog(restorationError.args.getModular(resourceManager)) + if (!baseNodeIterator.hasNext()) { + walletServiceLauncher.stopAndDelete() + showModularDialog(restorationError.args.getModular(resourceManager)) + } } private fun onSuccessRestoration() { From 62b737d83bd5e41f07dbbddc9f5aeeece63c98e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Sun, 6 Oct 2024 13:54:01 +0200 Subject: [PATCH 15/32] Increase app's version to 0.27.1(308) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a794b1acd..39c02000d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.coroutines_version = '1.8.0' // build & version - ext.buildNumber = 307 + ext.buildNumber = 308 ext.versionNumber = "0.27.1" // JNI libs From 47a595931939be0ff1e71e37952b89584cc457d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Mon, 7 Oct 2024 16:42:20 +0200 Subject: [PATCH 16/32] Fix issue with deleting wallet --- .../walletManager/WalletFileUtil.kt} | 9 ++------ .../walletManager/WalletManager.kt | 7 ++++-- .../data/sharedPrefs/CorePrefRepository.kt | 11 +++++---- .../logging/BugReportingService.kt | 4 ++-- .../logging/SentryLogAdapter.kt | 4 ++-- .../CustomTxNotificationViewHolder.kt | 6 ++--- .../wallet/notification/NotificationHelper.kt | 4 ++-- .../notification/TxCanceledViewHolder.kt | 4 ++-- .../wallet/service/service/WalletService.kt | 7 +----- .../service/service/WalletServiceLauncher.kt | 4 ++-- .../activity/OnboardingFlowActivity.kt | 9 ++------ .../ChooseRestoreOptionViewModel.kt | 4 ++-- .../EnterRestorationPasswordViewModel.kt | 4 ++-- .../send/addAmount/AddAmountFragment.kt | 4 ++-- .../addAmount/keyboard/KeyboardController.kt | 6 ++--- .../allSettings/AllSettingsViewModel.kt | 2 ++ .../deleteWallet/DeleteWalletFragment.kt | 18 +++------------ .../deleteWallet/DeleteWalletViewModel.kt | 17 ++++++++++++-- .../fragment/settings/logs/LogFilesManager.kt | 4 ++-- .../logs/logFiles/LogFilesViewModel.kt | 6 ++--- .../NetworkSelectionViewModel.kt | 4 ++-- .../ui/fragment/splash/SplashActivity.kt | 8 +++---- .../wallet/ui/fragment/tx/HomeFragment.kt | 4 ++-- .../tx/adapter/CommonTxListViewHolder.kt | 4 ++-- .../fragment/tx/details/TxDetailsFragment.kt | 6 ++--- .../BalanceViewController.kt | 6 ++--- .../list/adapters/UtxosTileListViewHolder.kt | 4 ++-- .../list/adapters/UtxosViewHolderItem.kt | 4 ++-- .../utxos/list/module/UtxoAmountModuleView.kt | 4 ++-- .../utxos/list/module/UtxoSplitModuleView.kt | 8 +++---- .../res/layout/fragment_delete_wallet.xml | 23 ++++++++----------- build.gradle | 2 +- 32 files changed, 100 insertions(+), 111 deletions(-) rename app/src/main/java/com/tari/android/wallet/{util/WalletUtil.kt => application/walletManager/WalletFileUtil.kt} (96%) diff --git a/app/src/main/java/com/tari/android/wallet/util/WalletUtil.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletFileUtil.kt similarity index 96% rename from app/src/main/java/com/tari/android/wallet/util/WalletUtil.kt rename to app/src/main/java/com/tari/android/wallet/application/walletManager/WalletFileUtil.kt index 5c94f9263..6b4e1c6f8 100644 --- a/app/src/main/java/com/tari/android/wallet/util/WalletUtil.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletFileUtil.kt @@ -30,7 +30,7 @@ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.tari.android.wallet.util +package com.tari.android.wallet.application.walletManager import com.tari.android.wallet.data.WalletConfig import java.io.File @@ -38,12 +38,7 @@ import java.math.RoundingMode import java.text.DecimalFormat import java.util.* -/** - * Wallet utility functions. - * - * @author The Tari Development Team - */ -object WalletUtil { +object WalletFileUtil { val balanceFormatter = DecimalFormat("#,##0.00").apply { roundingMode = RoundingMode.FLOOR diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index f8173f810..fad7d9ca5 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -85,7 +85,6 @@ import com.tari.android.wallet.tor.TorProxyManager import com.tari.android.wallet.tor.TorProxyStateHandler import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.util.Constants -import com.tari.android.wallet.util.WalletUtil import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -256,6 +255,10 @@ class WalletManager @Inject constructor( return walletInstance?.startRecovery(baseNodeFFI, recoveryOutputMessage) ?: false } + fun clearWalletFiles() { + WalletFileUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) + } + private fun startWallet() { if (walletState.value is WalletState.NotReady || walletState.value is WalletState.Failed) { logger.i("Initialize wallet started") @@ -324,7 +327,7 @@ class WalletManager @Inject constructor( private fun initWallet() { if (walletInstance == null) { // store network info in shared preferences if it's a new wallet - val isNewInstallation = !WalletUtil.walletExists(walletConfig) + val isNewInstallation = !WalletFileUtil.walletExists(walletConfig) val passphrase = securityPrefRepository.databasePassphrase.takeIf { !it.isNullOrEmpty() } ?: corePrefRepository.generateDatabasePassphrase().also { securityPrefRepository.databasePassphrase = it } diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt index 3cfa5c706..015453816 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt @@ -82,7 +82,6 @@ class CorePrefRepository @Inject constructor( const val SURNAME = "tari_wallet_surname_" const val ONBOARDING_STARTED = "tari_wallet_onboarding_started" const val ONBOARDING_AUTH_SETUP_COMPLETED = "tari_wallet_onboarding_auth_setup_completed" - const val ACTION_MENU_SIDE = "tari_wallet_action_menu_side" const val ONBOARDING_AUTH_SETUP_STARTED = "tari_wallet_onboarding_auth_setup_started" const val ONBOARDING_COMPLETED = "tari_wallet_onboarding_completed" const val ONBOARDING_DISPLAYED_AT_HOME = "tari_wallet_onboarding_displayed_at_home" @@ -105,8 +104,6 @@ class CorePrefRepository @Inject constructor( var onboardingAuthSetupCompleted: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.ONBOARDING_AUTH_SETUP_COMPLETED)) - var actionMenuSide: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.ACTION_MENU_SIDE)) - val onboardingAuthWasInterrupted: Boolean get() = onboardingAuthSetupStarted && (!onboardingAuthSetupCompleted || securityPrefRepository.pinCode == null) @@ -118,7 +115,13 @@ class CorePrefRepository @Inject constructor( var isDataCleared: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.IS_DATA_CLEARED), true) val walletAddress: TariWalletAddress - get() = TariWalletAddress.fromBase58(walletAddressBase58!!) + get() = walletAddressBase58?.let { TariWalletAddress.fromBase58(it) } ?: error("Wallet address is not set to shared preferences") + + /** + * Sometimes the wallet address is not set to the shared preferences (e.g. after a wallet removing). + * TODO: Investigate why the app accesses the wallet address when it is not set + */ + fun walletAddressExists(): Boolean = walletAddressBase58 != null fun clear() { baseNodeSharedRepository.clear() diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/BugReportingService.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/BugReportingService.kt index 694fb1eec..dd6e1ba51 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/BugReportingService.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/BugReportingService.kt @@ -34,7 +34,7 @@ package com.tari.android.wallet.infrastructure.logging import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import io.sentry.Attachment import io.sentry.Sentry import io.sentry.UserFeedback @@ -72,7 +72,7 @@ class BugReportingService @Inject constructor(private val sharedPrefsWrapper: Co } val fileOutStream = FileOutputStream(zipFile) // zip! - val allLogFiles = WalletUtil.getLogFilesFromDirectory(logFilesDirPath) + val allLogFiles = WalletFileUtil.getLogFilesFromDirectory(logFilesDirPath) ZipOutputStream(BufferedOutputStream(fileOutStream)).use { out -> for (file in allLogFiles) { FileInputStream(file).use { inputStream -> diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/SentryLogAdapter.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/SentryLogAdapter.kt index 5aae4d368..fd18eeab3 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/logging/SentryLogAdapter.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/logging/SentryLogAdapter.kt @@ -4,7 +4,7 @@ import com.orhanobut.logger.LogAdapter import com.orhanobut.logger.Logger import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.sentry.SentryPrefRepository -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.welie.blessed.BluetoothPeripheralManager import io.sentry.Attachment import io.sentry.Breadcrumb @@ -30,7 +30,7 @@ class SentryLogAdapter(val walletConfig: WalletConfig, if (priority == Logger.ERROR) { localScope.launch(Dispatchers.IO) { try { - val files = WalletUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() + val files = WalletFileUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() val lines = files.firstOrNull()?.inputStream()?.bufferedReader()?.readLines()?.takeLast(100)?.joinToString("\n") val breadcrumb = Breadcrumb(message).apply { diff --git a/app/src/main/java/com/tari/android/wallet/notification/CustomTxNotificationViewHolder.kt b/app/src/main/java/com/tari/android/wallet/notification/CustomTxNotificationViewHolder.kt index 1eb193c4c..d05d84750 100644 --- a/app/src/main/java/com/tari/android/wallet/notification/CustomTxNotificationViewHolder.kt +++ b/app/src/main/java/com/tari/android/wallet/notification/CustomTxNotificationViewHolder.kt @@ -40,7 +40,7 @@ import com.tari.android.wallet.R import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariContact import com.tari.android.wallet.model.Tx -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.util.addressFirstEmojis import com.tari.android.wallet.util.addressLastEmojis import com.tari.android.wallet.util.addressPrefixEmojis @@ -92,7 +92,7 @@ class CustomTxNotificationViewHolder(val context: Context, tx: Tx) : RemoteViews if (deviceIsLocked) { setTextViewText(R.id.notification_tx_received_txt_positive_amount, context.getString(R.string.common_new_uppercase)) } else { - val formattedValue = "+" + WalletUtil.amountFormatter.format(amount.tariValue) + val formattedValue = "+" + WalletFileUtil.amountFormatter.format(amount.tariValue) setTextViewText(R.id.notification_tx_received_txt_positive_amount, formattedValue) } setViewVisibility(R.id.notification_tx_received_txt_positive_amount, View.VISIBLE) @@ -103,7 +103,7 @@ class CustomTxNotificationViewHolder(val context: Context, tx: Tx) : RemoteViews if (deviceIsLocked) { setTextViewText(R.id.notification_tx_received_txt_negative_amount, context.getString(R.string.common_new_uppercase)) } else { - val formattedValue = "-" + WalletUtil.amountFormatter.format(amount.tariValue) + val formattedValue = "-" + WalletFileUtil.amountFormatter.format(amount.tariValue) setTextViewText(R.id.notification_tx_received_txt_negative_amount, formattedValue) } setViewVisibility(R.id.notification_tx_received_txt_negative_amount, View.VISIBLE) diff --git a/app/src/main/java/com/tari/android/wallet/notification/NotificationHelper.kt b/app/src/main/java/com/tari/android/wallet/notification/NotificationHelper.kt index 95c8db7c4..7d97f5d1c 100644 --- a/app/src/main/java/com/tari/android/wallet/notification/NotificationHelper.kt +++ b/app/src/main/java/com/tari/android/wallet/notification/NotificationHelper.kt @@ -50,7 +50,7 @@ import com.tari.android.wallet.model.CancelledTx import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.TxId import com.tari.android.wallet.ui.fragment.home.HomeDeeplinkScreens -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import javax.inject.Inject import javax.inject.Singleton @@ -132,7 +132,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { val notificationTitle = context.getString(R.string.notification_tx_received_title) // format spannable string val formattedAmount = if (tx.amount.tariValue.toDouble() % 1 == 0.toDouble()) tx.amount.tariValue.toBigInteger().toString() - else WalletUtil.amountFormatter.format(tx.amount.tariValue) + else WalletFileUtil.amountFormatter.format(tx.amount.tariValue) val notificationBody = context.getString(R.string.notification_tx_received_description_format, formattedAmount) val layout = CustomTxNotificationViewHolder(context, tx) val intent = Intent(context, NotificationBroadcastReceiver::class.java).apply { diff --git a/app/src/main/java/com/tari/android/wallet/notification/TxCanceledViewHolder.kt b/app/src/main/java/com/tari/android/wallet/notification/TxCanceledViewHolder.kt index 68efb0421..edea586b1 100644 --- a/app/src/main/java/com/tari/android/wallet/notification/TxCanceledViewHolder.kt +++ b/app/src/main/java/com/tari/android/wallet/notification/TxCanceledViewHolder.kt @@ -37,7 +37,7 @@ import android.content.Context import android.widget.RemoteViews import com.tari.android.wallet.R import com.tari.android.wallet.model.CancelledTx -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil class TxCanceledViewHolder(context: Context, tx: CancelledTx) : RemoteViews(context.packageName, R.layout.notification_remote_tx_canceled) { @@ -48,7 +48,7 @@ class TxCanceledViewHolder(context: Context, tx: CancelledTx) : val deviceIsLocked = keyguardManager?.isDeviceLocked ?: true val amount = if (deviceIsLocked) context.getString(R.string.common_new_uppercase) - else WalletUtil.amountFormatter.format(tx.amount.tariValue) + else WalletFileUtil.amountFormatter.format(tx.amount.tariValue) setTextViewText(R.id.notification_tx_canceled_amount_text_view, amount) } diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt index a9edb918c..9c910b94e 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt @@ -42,7 +42,6 @@ import com.orhanobut.logger.Logger import com.tari.android.wallet.application.TariWalletApplication import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.application.walletManager.doOnWalletStarted -import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.di.DiContainer import com.tari.android.wallet.ffi.FFIWallet @@ -55,7 +54,6 @@ import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.S import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.fragment.settings.logs.LogFilesManager import com.tari.android.wallet.util.Constants -import com.tari.android.wallet.util.WalletUtil import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -76,9 +74,6 @@ import javax.inject.Inject */ class WalletService : Service() { - @Inject - lateinit var walletConfig: WalletConfig - @Inject lateinit var app: TariWalletApplication @@ -173,7 +168,7 @@ class WalletService : Service() { } private fun deleteWallet() { - WalletUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) + walletManager.clearWalletFiles() sharedPrefsWrapper.clear() backupManager.turnOffAll() } diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt index e22165ec8..12141b0ad 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt @@ -6,7 +6,7 @@ import androidx.core.content.ContextCompat import com.tari.android.wallet.application.TariWalletApplication import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil class WalletServiceLauncher( private val context: Context, @@ -14,7 +14,7 @@ class WalletServiceLauncher( val tariSettingsSharedRepository: TariSettingsPrefRepository ) { fun startIfExist() { - if (WalletUtil.walletExists(walletConfig)) { + if (WalletFileUtil.walletExists(walletConfig)) { startService() } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt index 48edfba67..4ad0978c8 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt @@ -42,7 +42,6 @@ import androidx.lifecycle.lifecycleScope import com.tari.android.wallet.R import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.application.walletManager.doOnWalletFailed -import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.databinding.ActivityOnboardingFlowBinding import com.tari.android.wallet.di.DiContainer.appComponent @@ -57,7 +56,6 @@ import com.tari.android.wallet.ui.fragment.onboarding.createWallet.CreateWalletF import com.tari.android.wallet.ui.fragment.onboarding.inroduction.IntroductionFragment import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthFragment import com.tari.android.wallet.ui.fragment.settings.networkSelection.NetworkSelectionFragment -import com.tari.android.wallet.util.WalletUtil import kotlinx.coroutines.launch import javax.inject.Inject @@ -73,9 +71,6 @@ import javax.inject.Inject */ class OnboardingFlowActivity : CommonActivity(), OnboardingFlowListener { - @Inject - lateinit var walletConfig: WalletConfig - @Inject lateinit var corePrefRepository: CorePrefRepository @@ -105,7 +100,7 @@ class OnboardingFlowActivity : CommonActivity { + if (!settingsRepository.walletAddressExists()) return emptyList() // Return empty list if this method called after wallet is deleted + val versionText = TariVersionModel(networkRepository).versionInfo val alias = settingsRepository.firstName.orEmpty() + " " + settingsRepository.lastName.orEmpty() diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletFragment.kt index 35bb524f2..49dcc9c59 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletFragment.kt @@ -38,16 +38,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope import com.tari.android.wallet.databinding.FragmentDeleteWalletBinding import com.tari.android.wallet.extension.observe import com.tari.android.wallet.ui.common.CommonFragment import com.tari.android.wallet.ui.extension.ThrottleClick import com.tari.android.wallet.ui.extension.visible import com.tari.android.wallet.ui.fragment.onboarding.activity.OnboardingFlowActivity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext class DeleteWalletFragment : CommonFragment() { @@ -71,21 +67,13 @@ class DeleteWalletFragment : CommonFragment() + val showProgress = SingleLiveEvent() + val goToSplash = SingleLiveEvent() @Inject lateinit var walletServiceLauncher: WalletServiceLauncher @@ -26,10 +29,20 @@ class DeleteWalletViewModel : CommonViewModel() { HeadModule(resourceManager.getString(R.string.delete_wallet_confirmation_title)), BodyModule(resourceManager.getString(R.string.delete_wallet_confirmation_description)), ButtonModule(resourceManager.getString(R.string.common_confirm), ButtonStyle.Warning) { - deleteWallet.postValue(Unit) + onDeleteWalletClicked() hideDialog() }, ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close) ) } + + private fun onDeleteWalletClicked() { + showProgress.postValue(Unit) + launchOnIo { + walletServiceLauncher.stopAndDelete() + launchOnMain { + goToSplash.postValue(Unit) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/LogFilesManager.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/LogFilesManager.kt index e8c840ae5..d137a04a6 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/LogFilesManager.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/LogFilesManager.kt @@ -5,7 +5,7 @@ import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.event.Event import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import kotlinx.coroutines.launch import javax.inject.Inject @@ -22,7 +22,7 @@ class LogFilesManager : CommonViewModel() { } private fun manage() = viewModelScope.launch { - val files = WalletUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() + val files = WalletFileUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() if (files.size > MAX_FILES) { files.sortedByDescending { it.lastModified() }.drop(10).forEach { fileToDelete -> runCatching { fileToDelete.delete() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/logFiles/LogFilesViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/logFiles/LogFilesViewModel.kt index 982e24aa8..274cd7dce 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/logFiles/LogFilesViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/logs/logFiles/LogFilesViewModel.kt @@ -7,7 +7,7 @@ import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.common.recyclerView.CommonViewHolderItem import com.tari.android.wallet.ui.common.recyclerView.items.DividerViewHolderItem import com.tari.android.wallet.ui.fragment.settings.logs.logFiles.adapter.LogFileViewHolderItem -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import java.io.File import java.text.DecimalFormat import javax.inject.Inject @@ -26,7 +26,7 @@ class LogFilesViewModel : CommonViewModel() { init { component.inject(this) - val files = WalletUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() + val files = WalletFileUtil.getLogFilesFromDirectory(walletConfig.getWalletLogFilesDirPath()).toMutableList() val wholeList = files.map { listOf(LogFileViewHolderItem(getFileName(it), it) { goNext.postValue(it.file) }, DividerViewHolderItem()) } .flatten() .toMutableList() @@ -35,7 +35,7 @@ class LogFilesViewModel : CommonViewModel() { private fun getFileName(file: File): String = file.name + " - " + getReadableFileSize(file.length()) - private fun getReadableFileSize(size: Long): String? { + private fun getReadableFileSize(size: Long): String { if (size <= 0) return "0" val units = arrayOf("B", "KB", "MB", "GB", "TB") val digitGroups = (log10(size.toDouble()) / log10(bytesInKilo)).toInt() diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt index a2c0c45d2..a744f76bc 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/networkSelection/NetworkSelectionViewModel.kt @@ -15,7 +15,7 @@ import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.common.recyclerView.CommonViewHolderItem import com.tari.android.wallet.ui.dialog.confirm.ConfirmDialogArgs import com.tari.android.wallet.ui.fragment.settings.networkSelection.networkItem.NetworkViewHolderItem -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import javax.inject.Inject class NetworkSelectionViewModel : CommonViewModel() { @@ -49,7 +49,7 @@ class NetworkSelectionViewModel : CommonViewModel() { return } - if (WalletUtil.walletExists(walletConfig)) { + if (WalletFileUtil.walletExists(walletConfig)) { showModularDialog( ConfirmDialogArgs( title = resourceManager.getString(R.string.all_settings_select_network_confirm_title), diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt index 19e6e9a8c..cb449fbb6 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt @@ -38,6 +38,7 @@ import android.os.Bundle import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.coroutineScope +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.application.walletManager.doOnWalletNotReady import com.tari.android.wallet.data.WalletConfig @@ -50,7 +51,6 @@ import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.fragment.auth.AuthActivity import com.tari.android.wallet.ui.fragment.onboarding.activity.OnboardingFlowActivity -import com.tari.android.wallet.util.WalletUtil import kotlinx.coroutines.launch import javax.inject.Inject @@ -93,10 +93,10 @@ class SplashActivity : AppCompatActivity() { changeNetwork() } - val exists = WalletUtil.walletExists(walletConfig) && sharedPrefsRepository.onboardingAuthSetupCompleted - if (WalletUtil.walletExists(walletConfig) && !sharedPrefsRepository.onboardingAuthSetupCompleted) { + val exists = WalletFileUtil.walletExists(walletConfig) && sharedPrefsRepository.onboardingAuthSetupCompleted + if (WalletFileUtil.walletExists(walletConfig) && !sharedPrefsRepository.onboardingAuthSetupCompleted) { // in cases interrupted restoration - WalletUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) + walletManager.clearWalletFiles() sharedPrefsRepository.clear() } if (securityPrefRepository.pinCode == null) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt index 2f43fa93c..8c1cbd674 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt @@ -63,7 +63,7 @@ import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.ui.fragment.tx.adapter.TxListHomeViewHolder import com.tari.android.wallet.ui.fragment.tx.questionMark.QuestionMarkViewModel import com.tari.android.wallet.ui.fragment.tx.ui.balanceController.BalanceViewController -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil class HomeFragment : CommonFragment() { @@ -161,7 +161,7 @@ class HomeFragment : CommonFragment( private fun updateBalanceInfoUI(restart: Boolean) { val balanceInfo = viewModel.balanceInfo.value!! - val availableBalance = WalletUtil.balanceFormatter.format(balanceInfo.availableBalance.tariValue) + val availableBalance = WalletFileUtil.balanceFormatter.format(balanceInfo.availableBalance.tariValue) ui.availableBalance.text = availableBalance if (restart) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/adapter/CommonTxListViewHolder.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/adapter/CommonTxListViewHolder.kt index 189d80192..4c38f9a49 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/adapter/CommonTxListViewHolder.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/adapter/CommonTxListViewHolder.kt @@ -19,7 +19,7 @@ import com.tari.android.wallet.ui.extension.gone import com.tari.android.wallet.ui.extension.string import com.tari.android.wallet.ui.extension.visible import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.util.addressFirstEmojis import com.tari.android.wallet.util.addressLastEmojis import com.tari.android.wallet.util.addressPrefixEmojis @@ -97,7 +97,7 @@ abstract class CommonTxListViewHolder Triple( diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt index 4c1ab3f26..7db831874 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt @@ -73,7 +73,7 @@ import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto import com.tari.android.wallet.ui.fragment.tx.details.gif.GifView import com.tari.android.wallet.ui.fragment.tx.details.gif.GifViewModel import com.tari.android.wallet.ui.fragment.tx.details.gif.TxState -import com.tari.android.wallet.util.WalletUtil +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.util.addressFirstEmojis import com.tari.android.wallet.util.addressLastEmojis import com.tari.android.wallet.util.addressPrefixEmojis @@ -176,7 +176,7 @@ class TxDetailsFragment : CommonFragment string(R.string.tx_detail_payment_cancelled) state.status == MINED_CONFIRMED || state.status == IMPORTED -> @@ -200,7 +200,7 @@ class TxDetailsFragment : CommonFragment(view) { @@ -20,7 +20,7 @@ class UtxosTileListViewHolder(view: ItemUtxosTileBinding) : CommonViewHolder - - - - @@ -37,13 +36,14 @@ android:textSize="14sp" app:customFont="medium" /> - - - - - - - - + + + - - + diff --git a/build.gradle b/build.gradle index 39c02000d..2d077ac19 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.coroutines_version = '1.8.0' // build & version - ext.buildNumber = 308 + ext.buildNumber = 309 ext.versionNumber = "0.27.1" // JNI libs From c45922eb2e259d0b94cff7ddb6368dd5efc1bcab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Tue, 8 Oct 2024 15:12:17 +0200 Subject: [PATCH 17/32] Add Not Ready Yet dialog to DialogManager --- .../android/wallet/ui/common/DialogManager.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt index 8251a8ad0..f4e5b2df0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt @@ -1,9 +1,13 @@ package com.tari.android.wallet.ui.common import android.content.Context +import com.tari.android.wallet.R import com.tari.android.wallet.ui.dialog.modular.ModularDialog import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs.DialogId +import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule +import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule +import com.tari.android.wallet.ui.dialog.modular.modules.imageModule.ImageModule import javax.inject.Inject import javax.inject.Singleton @@ -41,6 +45,18 @@ class DialogManager @Inject constructor() { fun isDialogShowing(dialogId: Int) = dialogId != DialogId.NO_ID && dialogQueue.any { it.args.dialogId == dialogId } + fun showNotReadyYetDialog(context: Context) { + replace( + context, ModularDialogArgs( + modules = listOf( + ImageModule(R.drawable.tari_construction), + HeadModule(context.getString(R.string.common_not_ready_yet_dialog_title)), + BodyModule(context.getString(R.string.common_not_ready_yet_dialog_description)), + ) + ) + ) + } + private fun getDialog(dialogId: Int): ModularDialog? = if (dialogId != DialogId.NO_ID) dialogQueue.firstOrNull { it.args.dialogId == dialogId } else null } From ca72d3eeccb3a4fcd1b46876645b61e565dee223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Tue, 8 Oct 2024 16:39:09 +0200 Subject: [PATCH 18/32] Add dialogs for paper wallet QR scanner, refactor wallet removing code --- .../application/deeplinks/DeeplinkManager.kt | 60 +++++++++++++++++-- .../walletManager/WalletManager.kt | 18 ++++++ .../infrastructure/backup/BackupManager.kt | 2 + .../wallet/service/service/WalletService.kt | 14 ----- .../service/service/WalletServiceLauncher.kt | 6 -- .../android/wallet/ui/common/DialogManager.kt | 5 ++ .../ui/dialog/modular/ModularDialogArgs.kt | 2 + .../wallet/ui/fragment/auth/AuthActivity.kt | 4 -- .../wallet/ui/fragment/auth/AuthViewModel.kt | 12 ++-- .../ui/fragment/auth/FeatureAuthFragment.kt | 4 -- .../ui/fragment/home/navigation/Navigation.kt | 2 +- .../fragment/home/navigation/TariNavigator.kt | 13 ++-- .../activity/OnboardingFlowActivity.kt | 9 ++- .../ui/fragment/qr/QrScannerActivity.kt | 12 +--- .../ui/fragment/qr/QrScannerViewModel.kt | 12 +++- .../ChooseRestoreOptionViewModel.kt | 4 +- .../inputSeedWords/InputSeedWordsViewModel.kt | 2 +- .../WalletRestoringFromSeedWordsViewModel.kt | 6 +- .../deleteWallet/DeleteWalletViewModel.kt | 7 +-- .../ui/fragment/splash/SplashActivity.kt | 17 +++--- .../wallet/ui/fragment/tx/HomeFragment.kt | 4 +- app/src/main/res/values/strings.xml | 6 ++ 22 files changed, 138 insertions(+), 83 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index 832a82b32..39a34041e 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -55,7 +55,7 @@ class DeeplinkManager @Inject constructor( is DeepLink.Send -> sendAction(deeplink, isQrData) is DeepLink.UserProfile -> addUserProfile(context, deeplink, isQrData) is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink, isQrData) } } @@ -69,7 +69,7 @@ class DeeplinkManager @Inject constructor( is DeepLink.Send -> sendAction(deeplink, isQrData) is DeepLink.UserProfile -> addContactsAction(deeplink.data()?.let { listOf(it) } ?: emptyList(), isQrData) is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(deeplink, isQrData) + is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink, isQrData) } } @@ -130,8 +130,48 @@ class DeeplinkManager @Inject constructor( } } - private fun showPaperWalletDialog(deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { - // TODO + private fun showPaperWalletDialog(context: Context, deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { + dialogManager.replace( + context = context, + args = ModularDialogArgs( + dialogId = DialogId.DEEPLINK_PAPER_WALLET, + modules = listOf( + HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_title)), + BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_body)), + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_sweep_funds_button), ButtonStyle.Normal) { + dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET) + dialogManager.showNotReadyYetDialog(context) + }, + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_replace_wallet_button), ButtonStyle.Normal) { + dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET) + showRememberToBackupDialog(context, deeplink) + }, + ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close), + ), + ) + ) + } + + private fun showRememberToBackupDialog(context: Context, deeplink: DeepLink.PaperWallet) { + dialogManager.replace( + context = context, + args = ModularDialogArgs( + dialogId = DialogId.DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP, + modules = listOf( + HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_remember_backup_title)), + BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_remember_backup_body)), + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_remember_backup_yes_button), ButtonStyle.Normal) { + dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP) + goToBackupAction() + }, + ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_remember_backup_no_button), ButtonStyle.Normal) { + dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP) + replaceWalletAction(deeplink) + }, + ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close), + ), + ) + ) } private fun DeepLink.AddBaseNode.data(): BaseNodeDto = BaseNodeDto.fromDeeplink(this) @@ -168,4 +208,16 @@ class DeeplinkManager @Inject constructor( baseNodesManager.setBaseNode(baseNodeDto) walletManager.syncBaseNode() } + + private fun goToBackupAction() { + navigator.let { + it.toAllSettings() + it.toBackupSettings(true) + } + } + + private fun replaceWalletAction(deeplink: DeepLink.PaperWallet) { + walletManager.deleteWallet() + navigator.navigate(Navigation.SplashScreen(deeplink.seedWords)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index fad7d9ca5..cbc51a7f0 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -80,9 +80,11 @@ import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.service.notification.NotificationService import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import com.tari.android.wallet.service.service.WalletService +import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.tor.TorConfig import com.tari.android.wallet.tor.TorProxyManager import com.tari.android.wallet.tor.TorProxyStateHandler +import com.tari.android.wallet.ui.common.DialogManager import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.util.Constants import io.reactivex.Observable @@ -128,6 +130,8 @@ class WalletManager @Inject constructor( private val notificationHelper: NotificationHelper, private val notificationService: NotificationService, private val walletRestorationStateHandler: WalletRestorationStateHandler, + private val walletServiceLauncher: WalletServiceLauncher, + private val dialogManager: DialogManager, @ApplicationScope private val applicationScope: CoroutineScope, ) : OutboundTxNotifier { @@ -255,6 +259,18 @@ class WalletManager @Inject constructor( return walletInstance?.startRecovery(baseNodeFFI, recoveryOutputMessage) ?: false } + fun deleteWallet() { + walletInstance?.destroy() + walletInstance = null + _walletState.update { WalletState.NotReady } + runOnMain { _walletEvent.send(WalletEvent.OnWalletRemove) } + clearWalletFiles() + corePrefRepository.clear() + dialogManager.dismissAll() + walletServiceLauncher.stop() + + } + fun clearWalletFiles() { WalletFileUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) } @@ -649,6 +665,8 @@ class WalletManager @Inject constructor( data class TxCancelled(val tx: CancelledTx) : WalletEvent() data class DirectSendResult(val txId: TxId, val status: TransactionSendStatus) : WalletEvent() } + + data object OnWalletRemove : WalletEvent() } } diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt index 0f0d0364f..f459330c2 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt @@ -106,6 +106,8 @@ class BackupManager @Inject constructor( is WalletEvent.Tx.TxFauxMinedUnconfirmed, is WalletEvent.Tx.DirectSendResult, is WalletEvent.Tx.TxCancelled -> trigger.onNext(Unit) + + is WalletEvent.OnWalletRemove -> turnOffAll() } } } diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt index 9c910b94e..ca9cc1419 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt @@ -50,7 +50,6 @@ import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.service.ServiceRestartBroadcastReceiver import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.START_ACTION import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.STOP_ACTION -import com.tari.android.wallet.service.service.WalletServiceLauncher.Companion.STOP_AND_DELETE_ACTION import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.fragment.settings.logs.LogFilesManager import com.tari.android.wallet.util.Constants @@ -128,13 +127,6 @@ class WalletService : Service() { when (intent.action) { START_ACTION -> startService() STOP_ACTION -> stopService(startId) - STOP_AND_DELETE_ACTION -> { - //todo total crutch. Service is auto-creating during the bind func. Need to refactor this first - DiContainer.appComponent.inject(this) - stopService(startId) - deleteWallet() - } - else -> throw RuntimeException("Unexpected intent action: ${intent.action}") } return START_NOT_STICKY @@ -167,12 +159,6 @@ class WalletService : Service() { lifecycleObserver?.let { ProcessLifecycleOwner.get().lifecycle.removeObserver(it) } } - private fun deleteWallet() { - walletManager.clearWalletFiles() - sharedPrefsWrapper.clear() - backupManager.turnOffAll() - } - private fun onWalletStarted(ffiWallet: FFIWallet) { wallet = ffiWallet lifecycleObserver = ServiceLifecycleCallbacks(wallet) diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt index 12141b0ad..277264715 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt @@ -31,8 +31,6 @@ class WalletServiceLauncher( fun stop() = ContextCompat.startForegroundService(context, getStopIntent(context)) - fun stopAndDelete() = ContextCompat.startForegroundService(context, getStopAndDeleteIntent(context)) - fun startOnAppForegrounded() { if (!tariSettingsSharedRepository.backgroundServiceTurnedOn) { start() @@ -49,14 +47,10 @@ class WalletServiceLauncher( private fun getStopIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = STOP_ACTION } - private fun getStopAndDeleteIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = STOP_AND_DELETE_ACTION } - - companion object { // intent actions const val START_ACTION = "START_SERVICE" const val STOP_ACTION = "STOP_SERVICE" - const val STOP_AND_DELETE_ACTION = "STOP_SERVICE_AND_DELETE_WALLET" } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt index f4e5b2df0..10107e33c 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt @@ -43,6 +43,11 @@ class DialogManager @Inject constructor() { dialogQueue.remove(dialogToDismiss) } + fun dismissAll() { + dialogQueue.forEach { it.dismiss() } + dialogQueue.clear() + } + fun isDialogShowing(dialogId: Int) = dialogId != DialogId.NO_ID && dialogQueue.any { it.args.dialogId == dialogId } fun showNotReadyYetDialog(context: Context) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt index 3067fc4d6..7f47ba4ad 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt @@ -15,5 +15,7 @@ data class ModularDialogArgs( const val SCREEN_RECORDING = 603 const val DEEPLINK_ADD_BASE_NODE = 604 const val DEEPLINK_ADD_CONTACTS = 605 + const val DEEPLINK_PAPER_WALLET = 606 + const val DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP = 606 } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthActivity.kt index 11fdc6817..4f1f08b79 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthActivity.kt @@ -76,10 +76,6 @@ class AuthActivity : CommonActivity() { setupUi() - observe(viewModel.goAuth) { - viewModel.walletServiceLauncher.start() - } - doAuth() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthViewModel.kt index 1fd27398e..404d77d32 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/AuthViewModel.kt @@ -7,7 +7,6 @@ import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.infrastructure.security.biometric.BiometricAuthenticationService import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle @@ -26,8 +25,6 @@ class AuthViewModel : CommonViewModel() { @Inject lateinit var migrationManager: MigrationManager - val goAuth = SingleLiveEvent() - init { component.inject(this) @@ -35,7 +32,7 @@ class AuthViewModel : CommonViewModel() { migrationManager.validateVersion( onValid = { launchOnMain { - goAuth.postValue(Unit) + walletServiceLauncher.start() } }, onError = { @@ -56,17 +53,16 @@ class AuthViewModel : CommonViewModel() { deleteWallet() }, ButtonModule(resourceManager.getString(R.string.ffi_validation_error_cancel), ButtonStyle.Close) { - goAuth.postValue(Unit) + walletServiceLauncher.start() hideDialog() } ) } private fun deleteWallet() { - // disable CTAs launchOnIo { - walletServiceLauncher.stopAndDelete() - navigation.postValue(Navigation.TxListNavigation.ToSplashScreen) + walletManager.deleteWallet() + tariNavigator.navigate(Navigation.SplashScreen()) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/FeatureAuthFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/FeatureAuthFragment.kt index f9f86d22d..6100452c8 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/FeatureAuthFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/auth/FeatureAuthFragment.kt @@ -73,10 +73,6 @@ class FeatureAuthFragment : CommonFragment? = null) : TxListNavigation() sealed class CustomBridgeNavigation : Navigation() { object UploadQrCode : CustomBridgeNavigation() @@ -38,7 +39,6 @@ sealed class Navigation { } sealed class TxListNavigation : Navigation() { - object ToSplashScreen : TxListNavigation() class ToTxDetails(val tx: Tx) : TxListNavigation() object ToChat : TxListNavigation() object ToAllSettings : TxListNavigation() diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 41420215b..9610e9542 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -156,7 +156,7 @@ class TariNavigator @Inject constructor( is TxListNavigation.ToSendWithDeeplink -> toSendWithDeeplink(navigation.sendDeeplink) is TxListNavigation.ToUtxos -> toUtxos() is TxListNavigation.ToAllSettings -> toAllSettings() - is TxListNavigation.ToSplashScreen -> toSplash() + is Navigation.SplashScreen -> toSplash(navigation.seedWords) is TxListNavigation.ToTransfer -> addFragment(TransferFragment()) is TxListNavigation.HomeTransactionHistory -> addFragment(HomeTransactionHistoryFragment()) is SendTxNavigation.OnSendTxFailure -> onSendTxFailure(navigation.isYat, navigation.txFailureReason) @@ -174,10 +174,11 @@ class TariNavigator @Inject constructor( } } - private fun toSplash() { - val intent = Intent(activity, OnboardingFlowActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - activity.startActivity(intent) + private fun toSplash(seedWords: List?) { + activity.startActivity(Intent(activity, OnboardingFlowActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(OnboardingFlowActivity.ARG_SEED_WORDS, seedWords?.toTypedArray()) + }) activity.finishAffinity() } @@ -220,7 +221,7 @@ class TariNavigator @Inject constructor( fun toAllSettings() = (activity as HomeActivity).ui.viewPager.setCurrentItem(INDEX_SETTINGS, NO_SMOOTH_SCROLL) - fun toBackupSettings(withAnimation: Boolean) = addFragment(BackupSettingsFragment(), withAnimation = withAnimation) + fun toBackupSettings(withAnimation: Boolean) = addFragment(BackupSettingsFragment.newInstance(), withAnimation = withAnimation) private fun toDeleteWallet() = addFragment(DeleteWalletFragment()) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt index 4ad0978c8..0c0acd2e7 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt @@ -100,7 +100,7 @@ class OnboardingFlowActivity : CommonActivity setAlternativeText(deepLink) + QrScannerSource.Home -> { + when (deepLink) { + is DeepLink.Send, + is DeepLink.UserProfile, + is DeepLink.Contacts, + is DeepLink.TorBridges, + is DeepLink.AddBaseNode -> setAlternativeText(deepLink) + + is DeepLink.PaperWallet -> returnResult(deepLink) + } + } QrScannerSource.TransactionSend -> { when (deepLink) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index 7e40cfbb4..a45624df4 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.application.walletManager.doOnWalletFailed import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.WalletConfig @@ -32,7 +33,6 @@ import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions -import com.tari.android.wallet.application.walletManager.WalletFileUtil import java.io.IOException import javax.inject.Inject @@ -158,7 +158,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { is WalletStartFailedException -> { logger.i("Restore failed: wallet start failed") launchOnMain { - walletServiceLauncher.stopAndDelete() + walletManager.deleteWallet() } val cause = WalletError(exception.cause) if (cause == WalletError.DatabaseDataError) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt index 5b1d0c1d5..7709c5992 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt @@ -177,7 +177,7 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() private fun onError(restorationError: RestorationError) = showModularDialog(restorationError.args.getModular(resourceManager)) private fun clear() { - walletServiceLauncher.stopAndDelete() + walletManager.deleteWallet() compositeDisposable.dispose() compositeDisposable = CompositeDisposable() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt index 253d090ca..06a10db6d 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt @@ -9,7 +9,6 @@ import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.recovery.WalletRestorationState import com.tari.android.wallet.recovery.WalletRestorationStateHandler -import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs @@ -20,9 +19,6 @@ import javax.inject.Inject class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { - @Inject - lateinit var walletServiceLauncher: WalletServiceLauncher - @Inject lateinit var baseNodesManager: BaseNodesManager @@ -97,7 +93,7 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private fun onError(restorationError: RestorationError) { if (!baseNodeIterator.hasNext()) { - walletServiceLauncher.stopAndDelete() + walletManager.deleteWallet() showModularDialog(restorationError.args.getModular(resourceManager)) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletViewModel.kt index 1f8de4752..33b474561 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/deleteWallet/DeleteWalletViewModel.kt @@ -3,23 +3,18 @@ package com.tari.android.wallet.ui.fragment.settings.deleteWallet import com.tari.android.wallet.R import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain -import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule -import javax.inject.Inject class DeleteWalletViewModel : CommonViewModel() { val showProgress = SingleLiveEvent() val goToSplash = SingleLiveEvent() - @Inject - lateinit var walletServiceLauncher: WalletServiceLauncher - init { component.inject(this) } @@ -39,7 +34,7 @@ class DeleteWalletViewModel : CommonViewModel() { private fun onDeleteWalletClicked() { showProgress.postValue(Unit) launchOnIo { - walletServiceLauncher.stopAndDelete() + walletManager.deleteWallet() launchOnMain { goToSplash.postValue(Unit) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt index cb449fbb6..c673e0bec 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/splash/SplashActivity.kt @@ -86,7 +86,7 @@ class SplashActivity : AppCompatActivity() { onBackPressedDispatcher.addCallback { } if (sharedPrefsRepository.checkIfIsDataCleared()) { - walletServiceLauncher.stopAndDelete() + walletManager.deleteWallet() } if (!networkRepository.isCurrentNetworkSupported()) { @@ -96,14 +96,17 @@ class SplashActivity : AppCompatActivity() { val exists = WalletFileUtil.walletExists(walletConfig) && sharedPrefsRepository.onboardingAuthSetupCompleted if (WalletFileUtil.walletExists(walletConfig) && !sharedPrefsRepository.onboardingAuthSetupCompleted) { // in cases interrupted restoration - walletManager.clearWalletFiles() + walletManager.deleteWallet() sharedPrefsRepository.clear() } - if (securityPrefRepository.pinCode == null) { - launchActivity(OnboardingFlowActivity::class.java) - return - } - launchActivity(if (exists) AuthActivity::class.java else OnboardingFlowActivity::class.java) + + launchActivity( + if (securityPrefRepository.pinCode == null || !exists) { + OnboardingFlowActivity::class.java + } else { + AuthActivity::class.java + } + ) } private fun changeNetwork() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt index 8c1cbd674..99e178e93 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragment.kt @@ -45,6 +45,7 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.databinding.FragmentHomeBinding import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.extension.observe @@ -63,7 +64,6 @@ import com.tari.android.wallet.ui.fragment.qr.QrScannerSource import com.tari.android.wallet.ui.fragment.tx.adapter.TxListHomeViewHolder import com.tari.android.wallet.ui.fragment.tx.questionMark.QuestionMarkViewModel import com.tari.android.wallet.ui.fragment.tx.ui.balanceController.BalanceViewController -import com.tari.android.wallet.application.walletManager.WalletFileUtil class HomeFragment : CommonFragment() { @@ -135,7 +135,7 @@ class HomeFragment : CommonFragment( @SuppressLint("ClickableViewAccessibility") private fun setupUI() = with(ui) { viewAllTxsButton.setOnClickListener { viewModel.navigation.postValue(Navigation.TxListNavigation.HomeTransactionHistory) } - qrCodeButton.setOnClickListener { QrScannerActivity.startScanner(requireActivity(), QrScannerSource.Home) } + qrCodeButton.setOnClickListener { QrScannerActivity.startScanner(this@HomeFragment, QrScannerSource.Home) } transactionsRecyclerView.adapter = adapter adapter.setClickListener(CommonAdapter.ItemClickListener { viewModel.processItemClick(it) }) transactionsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc4068a0e..0b880d1cd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -578,6 +578,12 @@ You’ve scanned a paper wallet!\nWhat would you like to do? Restore the wallet Don\'t restore right now + Sweep the funds + Replace current wallet + Remember to back up first! + Do you want to back up your current wallet before replacing it? If you don\’t back it up first, you will lose all the funds in your current wallet! + Yes, back it up + No, just replace it Restore With Seed Phrase From 0245c621876199b99b4c8a77a18f3e7cf40ac4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Wed, 9 Oct 2024 16:27:52 +0200 Subject: [PATCH 19/32] Pass seed words as a param instead of using a temp repo --- .../walletManager/WalletManager.kt | 33 ++++--- .../android/wallet/di/ApplicationComponent.kt | 4 +- .../wallet/model/seedPhrase/SeedPhrase.kt | 64 +++++++------ .../wallet/service/BootDeviceReceiver.kt | 2 +- .../ServiceRestartBroadcastReceiver.kt | 2 +- .../seedPhrase/SeedPhraseRepository.kt | 17 ---- .../wallet/service/service/WalletService.kt | 10 +- .../service/service/WalletServiceLauncher.kt | 35 +++---- .../ui/fragment/home/navigation/Navigation.kt | 14 +-- .../fragment/home/navigation/TariNavigator.kt | 38 ++------ .../ChooseRestoreOptionViewModel.kt | 3 +- .../EnterRestorationPasswordViewModel.kt | 3 +- .../inputSeedWords/InputSeedWordsViewModel.kt | 36 ++----- ...Fragment.kt => WalletRestoringFragment.kt} | 18 ++-- ...ewModel.kt => WalletRestoringViewModel.kt} | 96 ++++++++++--------- .../BackgroundServiceSettingsViewModel.kt | 2 +- ...ords.xml => fragment_wallet_restoring.xml} | 0 17 files changed, 164 insertions(+), 213 deletions(-) delete mode 100644 app/src/main/java/com/tari/android/wallet/service/seedPhrase/SeedPhraseRepository.kt rename app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/{WalletRestoringFromSeedWordsFragment.kt => WalletRestoringFragment.kt} (79%) rename app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/{WalletRestoringFromSeedWordsViewModel.kt => WalletRestoringViewModel.kt} (51%) rename app/src/main/res/layout/{fragment_wallet_restoring_from_seed_words.xml => fragment_wallet_restoring.xml} (100%) diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index cbc51a7f0..ffd1c397b 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -52,6 +52,7 @@ import com.tari.android.wallet.ffi.FFIByteVector import com.tari.android.wallet.ffi.FFICommsConfig import com.tari.android.wallet.ffi.FFIException import com.tari.android.wallet.ffi.FFIPublicKey +import com.tari.android.wallet.ffi.FFISeedWords import com.tari.android.wallet.ffi.FFITariBaseNodeState import com.tari.android.wallet.ffi.FFITariTransportConfig import com.tari.android.wallet.ffi.FFIWallet @@ -71,6 +72,7 @@ import com.tari.android.wallet.model.TransactionSendStatus import com.tari.android.wallet.model.Tx import com.tari.android.wallet.model.TxId import com.tari.android.wallet.model.fullBase58 +import com.tari.android.wallet.model.seedPhrase.SeedPhrase import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.recovery.WalletRestorationState import com.tari.android.wallet.recovery.WalletRestorationStateHandler @@ -78,7 +80,6 @@ import com.tari.android.wallet.service.baseNode.BaseNodeState import com.tari.android.wallet.service.baseNode.BaseNodeStateHandler import com.tari.android.wallet.service.baseNode.BaseNodeSyncState import com.tari.android.wallet.service.notification.NotificationService -import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import com.tari.android.wallet.service.service.WalletService import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.tor.TorConfig @@ -118,7 +119,6 @@ class WalletManager @Inject constructor( private val walletConfig: WalletConfig, private val torManager: TorProxyManager, private val corePrefRepository: CorePrefRepository, - private val seedPhraseRepository: SeedPhraseRepository, private val networkPrefRepository: NetworkPrefRepository, private val tariSettingsPrefRepository: TariSettingsPrefRepository, private val securityPrefRepository: SecurityPrefRepository, @@ -171,12 +171,14 @@ class WalletManager @Inject constructor( private var txBroadcastRestarted = false @Synchronized - fun start() { + fun start(seedWords: List?) { torManager.run() + val ffiSeedWords = SeedPhrase.createOrNull(seedWords) + applicationScope.launch { torProxyStateHandler.doOnTorReadyForWallet { - startWallet() + startWallet(ffiSeedWords) } } } @@ -259,29 +261,34 @@ class WalletManager @Inject constructor( return walletInstance?.startRecovery(baseNodeFFI, recoveryOutputMessage) ?: false } + fun onWalletRestored() { + corePrefRepository.onboardingCompleted = true + corePrefRepository.onboardingStarted = true + corePrefRepository.onboardingAuthSetupStarted = true + corePrefRepository.onboardingAuthSetupCompleted = false + corePrefRepository.onboardingDisplayedAtHome = true + tariSettingsPrefRepository.isRestoredWallet = true + } + fun deleteWallet() { walletInstance?.destroy() walletInstance = null _walletState.update { WalletState.NotReady } runOnMain { _walletEvent.send(WalletEvent.OnWalletRemove) } - clearWalletFiles() + WalletFileUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) corePrefRepository.clear() dialogManager.dismissAll() walletServiceLauncher.stop() } - fun clearWalletFiles() { - WalletFileUtil.clearWalletFiles(walletConfig.getWalletFilesDirPath()) - } - - private fun startWallet() { + private fun startWallet(ffiSeedWords: FFISeedWords?) { if (walletState.value is WalletState.NotReady || walletState.value is WalletState.Failed) { logger.i("Initialize wallet started") _walletState.update { WalletState.Initializing } applicationScope.launch { try { - initWallet() + initWallet(ffiSeedWords) _walletState.update { WalletState.Started } logger.i("Wallet was started") } catch (e: Exception) { @@ -340,7 +347,7 @@ class WalletManager @Inject constructor( /** * Initializes the wallet and sets the singleton instance in the wallet companion object. */ - private fun initWallet() { + private fun initWallet(ffiSeedWords: FFISeedWords?) { if (walletInstance == null) { // store network info in shared preferences if it's a new wallet val isNewInstallation = !WalletFileUtil.walletExists(walletConfig) @@ -353,7 +360,7 @@ class WalletManager @Inject constructor( commsConfig = getCommsConfig(), logPath = walletConfig.getWalletLogFilePath(), passphrase = passphrase, - seedWords = seedPhraseRepository.getPhrase()?.ffiSeedWords, + seedWords = ffiSeedWords, listener = object : FFIWalletListener { /** * All the callbacks are called on the FFI thread, so we need to switch to the main thread. diff --git a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt index 60ad6c4ea..4fb0f6de6 100644 --- a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt +++ b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt @@ -66,7 +66,7 @@ import com.tari.android.wallet.ui.fragment.restore.activity.WalletRestoreActivit import com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.ChooseRestoreOptionViewModel import com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword.EnterRestorationPasswordViewModel import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsViewModel -import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFromSeedWordsViewModel +import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringViewModel import com.tari.android.wallet.ui.fragment.send.addAmount.AddAmountViewModel import com.tari.android.wallet.ui.fragment.send.addNote.AddNoteViewModel import com.tari.android.wallet.ui.fragment.send.addNote.gif.ChooseGIFDialogFragment @@ -148,7 +148,7 @@ interface ApplicationComponent { fun inject(viewModel: ConnectionIndicatorViewModel) fun inject(viewModel: ChooseRestoreOptionViewModel) fun inject(viewModel: EnterRestorationPasswordViewModel) - fun inject(viewModel: WalletRestoringFromSeedWordsViewModel) + fun inject(viewModel: WalletRestoringViewModel) fun inject(viewModel: InputSeedWordsViewModel) fun inject(viewModel: VerifySeedPhraseViewModel) fun inject(viewModel: BackupSettingsViewModel) diff --git a/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt b/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt index ac150ace0..d3e133857 100644 --- a/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt +++ b/app/src/main/java/com/tari/android/wallet/model/seedPhrase/SeedPhrase.kt @@ -1,38 +1,12 @@ package com.tari.android.wallet.model.seedPhrase +import com.orhanobut.logger.Logger import com.tari.android.wallet.ffi.FFISeedWords class SeedPhrase { - var ffiSeedWords: FFISeedWords? = null - private set - - fun init(words: List): SeedPhraseCreationResult { - val ffiSeedWords = FFISeedWords() - - try { - for (seedWord in words) { - return when (ffiSeedWords.pushWord(seedWord)) { - SeedWordsWordPushResult.InvalidSeedWord -> SeedPhraseCreationResult.InvalidSeedWord - SeedWordsWordPushResult.SuccessfulPush -> continue - SeedWordsWordPushResult.SeedPhraseComplete -> { - this.ffiSeedWords = ffiSeedWords - SeedPhraseCreationResult.Success - } - - SeedWordsWordPushResult.InvalidSeedPhrase -> SeedPhraseCreationResult.InvalidSeedPhrase - } - } - } catch (e: Throwable) { - return SeedPhraseCreationResult.Failed(e) - } - - return SeedPhraseCreationResult.SeedPhraseNotCompleted - } - - sealed class SeedPhraseCreationResult { - data object Success : SeedPhraseCreationResult() + data class Success(val ffiSeedWords: FFISeedWords) : SeedPhraseCreationResult() data class Failed(val exception: Throwable) : SeedPhraseCreationResult() data object InvalidSeedPhrase : SeedPhraseCreationResult() data object SeedPhraseNotCompleted : SeedPhraseCreationResult() @@ -40,6 +14,40 @@ class SeedPhrase { } companion object { + private val logger + get() = Logger.t(SeedPhrase::class.simpleName) + const val SEED_PHRASE_LENGTH: Int = 24 + + fun create(words: List): SeedPhraseCreationResult { + val ffiSeedWords = FFISeedWords() + + try { + for (seedWord in words) { + return when (ffiSeedWords.pushWord(seedWord)) { + SeedWordsWordPushResult.InvalidSeedWord -> SeedPhraseCreationResult.InvalidSeedWord + SeedWordsWordPushResult.SuccessfulPush -> continue + SeedWordsWordPushResult.SeedPhraseComplete -> SeedPhraseCreationResult.Success(ffiSeedWords) + SeedWordsWordPushResult.InvalidSeedPhrase -> SeedPhraseCreationResult.InvalidSeedPhrase + } + } + } catch (e: Throwable) { + return SeedPhraseCreationResult.Failed(e) + } + + return SeedPhraseCreationResult.SeedPhraseNotCompleted + } + + fun createOrNull(words: List?): FFISeedWords? { + return words?.let { + when (val result = create(words)) { + is SeedPhraseCreationResult.Success -> result.ffiSeedWords + else -> { + logger.i("Seed phrase creation failed: $result") + null + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/BootDeviceReceiver.kt b/app/src/main/java/com/tari/android/wallet/service/BootDeviceReceiver.kt index 861b722b1..5a1624c7d 100644 --- a/app/src/main/java/com/tari/android/wallet/service/BootDeviceReceiver.kt +++ b/app/src/main/java/com/tari/android/wallet/service/BootDeviceReceiver.kt @@ -63,7 +63,7 @@ class BootDeviceReceiver : BroadcastReceiver() { val sharedPreferences = context.getSharedPreferences(ApplicationModule.sharedPrefsFileName, Context.MODE_PRIVATE) val networkRepository = NetworkPrefRepositoryImpl(sharedPreferences) val tariSettingsSharedRepository = TariSettingsPrefRepository(sharedPreferences, networkRepository) - WalletServiceLauncher(context, WalletConfig(context, networkRepository), tariSettingsSharedRepository).startIfExist() + WalletServiceLauncher(context, WalletConfig(context, networkRepository), tariSettingsSharedRepository).startIfWalletExists() } } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/ServiceRestartBroadcastReceiver.kt b/app/src/main/java/com/tari/android/wallet/service/ServiceRestartBroadcastReceiver.kt index e9aa062bb..32caf51fc 100644 --- a/app/src/main/java/com/tari/android/wallet/service/ServiceRestartBroadcastReceiver.kt +++ b/app/src/main/java/com/tari/android/wallet/service/ServiceRestartBroadcastReceiver.kt @@ -58,6 +58,6 @@ class ServiceRestartBroadcastReceiver : BroadcastReceiver() { val sharedPreferences = context.getSharedPreferences(ApplicationModule.sharedPrefsFileName, Context.MODE_PRIVATE) val networkRepository = NetworkPrefRepositoryImpl(sharedPreferences) val tariSettingsSharedRepository = TariSettingsPrefRepository(sharedPreferences, networkRepository) - WalletServiceLauncher(context, WalletConfig(context, networkRepository), tariSettingsSharedRepository).startIfExist() + WalletServiceLauncher(context, WalletConfig(context, networkRepository), tariSettingsSharedRepository).startIfWalletExists() } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/seedPhrase/SeedPhraseRepository.kt b/app/src/main/java/com/tari/android/wallet/service/seedPhrase/SeedPhraseRepository.kt deleted file mode 100644 index fb22354e6..000000000 --- a/app/src/main/java/com/tari/android/wallet/service/seedPhrase/SeedPhraseRepository.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tari.android.wallet.service.seedPhrase - -import com.tari.android.wallet.model.seedPhrase.SeedPhrase -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SeedPhraseRepository @Inject constructor() { - - private var seedPhrase: SeedPhrase? = null - - fun save(seedPhrase: SeedPhrase) { - this.seedPhrase = seedPhrase - } - - fun getPhrase(): SeedPhrase? = seedPhrase -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt index ca9cc1419..b1af66148 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletService.kt @@ -42,7 +42,6 @@ import com.orhanobut.logger.Logger import com.tari.android.wallet.application.TariWalletApplication import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.application.walletManager.doOnWalletStarted -import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.di.DiContainer import com.tari.android.wallet.ffi.FFIWallet import com.tari.android.wallet.infrastructure.backup.BackupManager @@ -82,9 +81,6 @@ class WalletService : Service() { @Inject lateinit var notificationHelper: NotificationHelper - @Inject - lateinit var sharedPrefsWrapper: CorePrefRepository - @Inject lateinit var walletManager: WalletManager @@ -125,14 +121,14 @@ class WalletService : Service() { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { startForeground() when (intent.action) { - START_ACTION -> startService() + START_ACTION -> startService(intent.getStringArrayExtra(WalletServiceLauncher.ARG_SEED_WORDS)?.toList()) STOP_ACTION -> stopService(startId) else -> throw RuntimeException("Unexpected intent action: ${intent.action}") } return START_NOT_STICKY } - private fun startService() { + private fun startService(seedWords: List?) { //todo total crutch. Service is auto-creating during the bind func. Need to refactor this first DiContainer.appComponent.inject(this) @@ -141,7 +137,7 @@ class WalletService : Service() { onWalletStarted(it) } } - walletManager.start() + walletManager.start(seedWords) logger.i("Wallet service started") } diff --git a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt index 277264715..50b28c6fa 100644 --- a/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt +++ b/app/src/main/java/com/tari/android/wallet/service/service/WalletServiceLauncher.kt @@ -4,32 +4,40 @@ import android.content.Context import android.content.Intent import androidx.core.content.ContextCompat import com.tari.android.wallet.application.TariWalletApplication +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.data.WalletConfig import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository -import com.tari.android.wallet.application.walletManager.WalletFileUtil +/** + * Wallet service launcher. Starts the wallet service that starts the wallet. + */ class WalletServiceLauncher( private val context: Context, val walletConfig: WalletConfig, val tariSettingsSharedRepository: TariSettingsPrefRepository ) { - fun startIfExist() { + fun startIfWalletExists(seedWords: List? = null) { if (WalletFileUtil.walletExists(walletConfig)) { - startService() + startService(seedWords) } } - fun start() { - if (tariSettingsSharedRepository.backgroundServiceTurnedOn || - !tariSettingsSharedRepository.backgroundServiceTurnedOn && TariWalletApplication.INSTANCE.get()?.isInForeground == true - ) { - startService() + // We can't pass a FFISeedWords object to the service, so we pass the seed words as a list of strings + fun start(seedWords: List? = null) { + val backgroundServiceTurnedOn = tariSettingsSharedRepository.backgroundServiceTurnedOn + val appInForeground = TariWalletApplication.INSTANCE.get()?.isInForeground == true + if (backgroundServiceTurnedOn || appInForeground) { + startService(seedWords) } } - private fun startService() = ContextCompat.startForegroundService(context, getStartIntent(context)) + private fun startService(seedWords: List?) = + ContextCompat.startForegroundService(context, Intent(context, WalletService::class.java).also { + it.action = START_ACTION + it.putExtra(ARG_SEED_WORDS, seedWords?.toTypedArray()) + }) - fun stop() = ContextCompat.startForegroundService(context, getStopIntent(context)) + fun stop() = ContextCompat.startForegroundService(context, Intent(context, WalletService::class.java).also { it.action = STOP_ACTION }) fun startOnAppForegrounded() { if (!tariSettingsSharedRepository.backgroundServiceTurnedOn) { @@ -43,14 +51,9 @@ class WalletServiceLauncher( } } - private fun getStartIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = START_ACTION } - - private fun getStopIntent(context: Context) = Intent(context, WalletService::class.java).also { it.action = STOP_ACTION } - companion object { - // intent actions const val START_ACTION = "START_SERVICE" const val STOP_ACTION = "STOP_SERVICE" + const val ARG_SEED_WORDS = "ARG_SEED_WORDS" } - } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt index 875d55841..7edb4e1c2 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt @@ -16,7 +16,7 @@ sealed class Navigation { class EnterPinCodeNavigation(val behavior: PinCodeScreenBehavior, val stashedPin: String? = null) : Navigation() object ChangeBiometrics : Navigation() object FeatureAuth : Navigation() - data class SplashScreen(val seedWords: List? = null) : TxListNavigation() + data class SplashScreen(val seedWords: List? = null, val clearTop: Boolean = true) : Navigation() sealed class CustomBridgeNavigation : Navigation() { object UploadQrCode : CustomBridgeNavigation() @@ -65,11 +65,6 @@ sealed class Navigation { data class OnSendTxSuccess(val isYat: Boolean, val txId: TxId) : SendTxNavigation() } - sealed class WalletRestoringFromSeedWordsNavigation : Navigation() { - object OnRestoreCompleted : WalletRestoringFromSeedWordsNavigation() - object OnRestoreFailed : WalletRestoringFromSeedWordsNavigation() - } - sealed class AllSettingsNavigation : Navigation() { object ToMyProfile : AllSettingsNavigation() object ToBugReporting : AllSettingsNavigation() @@ -88,18 +83,13 @@ sealed class Navigation { } sealed class InputSeedWordsNavigation : Navigation() { - object ToRestoreFormSeedWordsInProgress : InputSeedWordsNavigation() + object ToRestoreFromSeeds : InputSeedWordsNavigation() object ToBaseNodeSelection : InputSeedWordsNavigation() } - sealed class EnterRestorationPasswordNavigation : Navigation() { - object OnRestore : EnterRestorationPasswordNavigation() - } - sealed class ChooseRestoreOptionNavigation : Navigation() { object ToEnterRestorePassword : ChooseRestoreOptionNavigation() data class ToRestoreWithRecoveryPhrase(val seedWords: List?) : ChooseRestoreOptionNavigation() - object OnRestoreCompleted : ChooseRestoreOptionNavigation() } sealed class ContactBookNavigation : Navigation() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 9610e9542..4b2b46e26 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -9,8 +9,6 @@ import com.tari.android.wallet.application.YatAdapter import com.tari.android.wallet.application.YatAdapter.ConnectedWallet import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.walletManager.WalletManager -import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository -import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsPrefRepository import com.tari.android.wallet.event.Event import com.tari.android.wallet.event.EventBus import com.tari.android.wallet.model.MicroTari @@ -48,20 +46,18 @@ import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.ChatNaviga import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.ChooseRestoreOptionNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.ContactBookNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.CustomBridgeNavigation -import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.EnterRestorationPasswordNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.InputSeedWordsNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.SendTxNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.TorBridgeNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.TxListNavigation import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.VerifySeedPhraseNavigation -import com.tari.android.wallet.ui.fragment.home.navigation.Navigation.WalletRestoringFromSeedWordsNavigation import com.tari.android.wallet.ui.fragment.onboarding.activity.OnboardingFlowActivity import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthFragment import com.tari.android.wallet.ui.fragment.pinCode.EnterPinCodeFragment import com.tari.android.wallet.ui.fragment.profile.WalletInfoFragment import com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword.EnterRestorationPasswordFragment import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsFragment -import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFromSeedWordsFragment +import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFragment import com.tari.android.wallet.ui.fragment.send.addAmount.AddAmountFragment import com.tari.android.wallet.ui.fragment.send.addNote.AddNoteFragment import com.tari.android.wallet.ui.fragment.send.common.TransactionData @@ -101,8 +97,6 @@ import javax.inject.Singleton // TODO: move navigation logic to only the navigate() method and make all navigation methods private @Singleton class TariNavigator @Inject constructor( - private val prefs: CorePrefRepository, - private val tariSettingsSharedRepository: TariSettingsPrefRepository, private val walletManager: WalletManager, private val yatAdapter: YatAdapter, private val networkConnection: NetworkConnectionStateHandler, @@ -115,6 +109,7 @@ class TariNavigator @Inject constructor( is Navigation.EnterPinCodeNavigation -> addFragment(EnterPinCodeFragment.newInstance(navigation.behavior, navigation.stashedPin)) is Navigation.ChangeBiometrics -> addFragment(ChangeBiometricsFragment()) is Navigation.FeatureAuth -> addFragment(FeatureAuthFragment()) + is Navigation.SplashScreen -> toSplash(navigation.seedWords, navigation.clearTop) is ContactBookNavigation.ToAddContact -> toAddContact() is ContactBookNavigation.ToContactDetails -> toContactDetails(navigation.contact) is ContactBookNavigation.ToRequestTari -> toRequestTariFromContact(navigation.contact) @@ -126,7 +121,6 @@ class TariNavigator @Inject constructor( is ContactBookNavigation.ToAddPhoneContact -> toAddPhoneContact() is ContactBookNavigation.ToSelectTariUser -> addFragment(SelectUserContactFragment.newInstance()) is ChooseRestoreOptionNavigation.ToEnterRestorePassword -> toEnterRestorePassword() - is ChooseRestoreOptionNavigation.OnRestoreCompleted -> onRestoreCompleted() is ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase -> toRestoreWithRecoveryPhrase(navigation.seedWords) is AllSettingsNavigation.ToBugReporting -> DebugActivity.launch(activity, DebugNavigation.BugReport) is AllSettingsNavigation.ToMyProfile -> toMyProfile() @@ -142,11 +136,8 @@ class TariNavigator @Inject constructor( is AllSettingsNavigation.ToDataCollection -> addFragment(DataCollectionFragment()) is AllSettingsNavigation.ToThemeSelection -> toThemeSelection() is AllSettingsNavigation.ToRequestTari -> addFragment(RequestTariFragment.newInstance()) - is EnterRestorationPasswordNavigation.OnRestore -> onRestoreCompleted() - is InputSeedWordsNavigation.ToRestoreFormSeedWordsInProgress -> toRestoreFromSeedWordsInProgress() + is InputSeedWordsNavigation.ToRestoreFromSeeds -> addFragment(WalletRestoringFragment.newInstance()) is InputSeedWordsNavigation.ToBaseNodeSelection -> toBaseNodeSelection() - is WalletRestoringFromSeedWordsNavigation.OnRestoreCompleted -> onRestoreCompleted() - is WalletRestoringFromSeedWordsNavigation.OnRestoreFailed -> onBackPressed() is AddAmountNavigation.OnAmountExceedsActualAvailableBalance -> onAmountExceedsActualAvailableBalance() is AddAmountNavigation.ContinueToAddNote -> continueToAddNote(navigation.transactionData) is AddAmountNavigation.ContinueToFinalizing -> continueToFinalizeSendTx(navigation.transactionData) @@ -156,7 +147,6 @@ class TariNavigator @Inject constructor( is TxListNavigation.ToSendWithDeeplink -> toSendWithDeeplink(navigation.sendDeeplink) is TxListNavigation.ToUtxos -> toUtxos() is TxListNavigation.ToAllSettings -> toAllSettings() - is Navigation.SplashScreen -> toSplash(navigation.seedWords) is TxListNavigation.ToTransfer -> addFragment(TransferFragment()) is TxListNavigation.HomeTransactionHistory -> addFragment(HomeTransactionHistoryFragment()) is SendTxNavigation.OnSendTxFailure -> onSendTxFailure(navigation.isYat, navigation.txFailureReason) @@ -174,9 +164,10 @@ class TariNavigator @Inject constructor( } } - private fun toSplash(seedWords: List?) { + private fun toSplash(seedWords: List? = null, clearTop: Boolean = true) { activity.startActivity(Intent(activity, OnboardingFlowActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + flags = if (clearTop) Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + else Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra(OnboardingFlowActivity.ARG_SEED_WORDS, seedWords?.toTypedArray()) }) activity.finishAffinity() @@ -191,23 +182,6 @@ class TariNavigator @Inject constructor( private fun toRestoreWithRecoveryPhrase(seedWords: List?) = addFragment(InputSeedWordsFragment.createFragment(seedWords)) - private fun toRestoreFromSeedWordsInProgress() = addFragment(WalletRestoringFromSeedWordsFragment.newInstance()) - - private fun onRestoreCompleted() { - // wallet restored, setup shared prefs accordingly - prefs.onboardingCompleted = true - prefs.onboardingStarted = true - prefs.onboardingAuthSetupStarted = true - prefs.onboardingAuthSetupCompleted = false - prefs.onboardingDisplayedAtHome = true - tariSettingsSharedRepository.isRestoredWallet = true - - activity.finish() - activity.startActivity(Intent(this.activity, OnboardingFlowActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - }) - } - fun onBackPressed() = activity.onBackPressed() fun toTxDetails(tx: Tx? = null, txId: TxId? = null) = activity.addFragment(TxDetailsFragment().apply { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index a45624df4..b7165ef8f 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -77,7 +77,8 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { backupPrefRepository.updateOption(dto) backupManager.backupNow() - navigation.postValue(Navigation.ChooseRestoreOptionNavigation.OnRestoreCompleted) + walletManager.onWalletRestored() + tariNavigator.navigate(Navigation.SplashScreen(clearTop = false)) } } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt index a5f104025..1e42c3da5 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/enterRestorationPassword/EnterRestorationPasswordViewModel.kt @@ -43,7 +43,8 @@ class EnterRestorationPasswordViewModel : CommonViewModel() { backupSettingsRepository.updateOption(dto) backupManager.backupNow() - navigation.postValue(Navigation.EnterRestorationPasswordNavigation.OnRestore) + walletManager.onWalletRestored() + tariNavigator.navigate(Navigation.SplashScreen(clearTop = false)) } } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt index 7709c5992..69b64ce33 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt @@ -15,7 +15,6 @@ import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.ffi.FFISeedWords import com.tari.android.wallet.model.WalletError import com.tari.android.wallet.model.seedPhrase.SeedPhrase -import com.tari.android.wallet.service.seedPhrase.SeedPhraseRepository import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.SingleLiveEvent @@ -40,9 +39,6 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() private var mnemonicList = mutableListOf() - @Inject - lateinit var seedPhraseRepository: SeedPhraseRepository - @Inject lateinit var walletServiceLauncher: WalletServiceLauncher @@ -110,13 +106,13 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() fun startRestoringWallet() { val words = _words.value!!.map { it.text.value!! } - val seedPhrase = SeedPhrase() - val result = seedPhrase.init(words) + when (SeedPhrase.create(words)) { + is SeedPhrase.SeedPhraseCreationResult.Success -> startRestoring(words) - if (result == SeedPhrase.SeedPhraseCreationResult.Success) { - startRestoring(seedPhrase) - } else { - handleSeedPhraseResult(result) + is SeedPhrase.SeedPhraseCreationResult.SeedPhraseNotCompleted -> onError(RestorationError.SeedPhraseTooShort(resourceManager)) + is SeedPhrase.SeedPhraseCreationResult.Failed -> onError(RestorationError.Unknown(resourceManager)) + is SeedPhrase.SeedPhraseCreationResult.InvalidSeedPhrase, + is SeedPhrase.SeedPhraseCreationResult.InvalidSeedWord -> onError(RestorationError.Invalid(resourceManager)) } } @@ -130,11 +126,9 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() } } - private fun startRestoring(seedPhrase: SeedPhrase) { + private fun startRestoring(seedWords: List) { _inProgress.postValue(true) - seedPhraseRepository.save(seedPhrase) - launchOnIo { walletManager.doOnWalletFailed { exception -> if (WalletError(exception) == WalletError.NoError) { @@ -149,7 +143,7 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() launchOnIo { walletManager.doOnWalletRunning { - tariNavigator.navigate(Navigation.InputSeedWordsNavigation.ToRestoreFormSeedWordsInProgress) + tariNavigator.navigate(Navigation.InputSeedWordsNavigation.ToRestoreFromSeeds) _inProgress.postValue(false) } } @@ -159,19 +153,7 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() baseNodesManager.setBaseNode(it) walletManager.syncBaseNode() } - walletServiceLauncher.start() - } - - private fun handleSeedPhraseResult(result: SeedPhrase.SeedPhraseCreationResult) { - val errorDialogArgs = when (result) { - is SeedPhrase.SeedPhraseCreationResult.Failed -> RestorationError.Unknown(resourceManager) - is SeedPhrase.SeedPhraseCreationResult.InvalidSeedPhrase, - is SeedPhrase.SeedPhraseCreationResult.InvalidSeedWord -> RestorationError.Invalid(resourceManager) - - is SeedPhrase.SeedPhraseCreationResult.SeedPhraseNotCompleted -> RestorationError.SeedPhraseTooShort(resourceManager) - else -> RestorationError.Unknown(resourceManager) - } - onError(errorDialogArgs) + walletServiceLauncher.start(seedWords) } private fun onError(restorationError: RestorationError) = showModularDialog(restorationError.args.getModular(resourceManager)) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt similarity index 79% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsFragment.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt index 8761f8d02..aa6f96583 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt @@ -37,21 +37,21 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels -import com.tari.android.wallet.databinding.FragmentWalletRestoringFromSeedWordsBinding -import com.tari.android.wallet.extension.observe +import com.tari.android.wallet.databinding.FragmentWalletRestoringBinding +import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.ui.common.CommonFragment -class WalletRestoringFromSeedWordsFragment : - CommonFragment() { +class WalletRestoringFragment : + CommonFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - FragmentWalletRestoringFromSeedWordsBinding.inflate(inflater, container, false).also { ui = it }.root + FragmentWalletRestoringBinding.inflate(inflater, container, false).also { ui = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) changeOnBackPressed(true) - val viewModel: WalletRestoringFromSeedWordsViewModel by viewModels() + val viewModel: WalletRestoringViewModel by viewModels() bindViewModel(viewModel) subscribeUI() @@ -60,15 +60,15 @@ class WalletRestoringFromSeedWordsFragment : } private fun subscribeUI() = with(viewModel) { - observe(recoveryState) { processRecoveryState(it) } + collectFlow(recoveryState) { processRecoveryState(it) } } - private fun processRecoveryState(state: WalletRestoringFromSeedWordsViewModel.RecoveryState) { + private fun processRecoveryState(state: WalletRestoringViewModel.RestorationState) { ui.statusLabel.text = state.status ui.progressLabel.text = state.progress } companion object { - fun newInstance() = WalletRestoringFromSeedWordsFragment() + fun newInstance() = WalletRestoringFragment() } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt similarity index 51% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt index 06a10db6d..8c47d87a7 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFromSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt @@ -1,7 +1,5 @@ package com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto @@ -13,11 +11,15 @@ import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.fragment.home.navigation.Navigation -import io.reactivex.disposables.CompositeDisposable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.withContext import java.text.DecimalFormat import javax.inject.Inject -class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { +class WalletRestoringViewModel : CommonViewModel() { @Inject lateinit var baseNodesManager: BaseNodesManager @@ -27,8 +29,8 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private lateinit var baseNodeIterator: Iterator - private val _recoveryState = MutableLiveData(RecoveryState.ConnectingToBaseNode(resourceManager)) - val recoveryState: LiveData = _recoveryState + private val _recoveryState = MutableStateFlow(RestorationState.ConnectingToBaseNode(resourceManager)) + val recoveryState = _recoveryState.asStateFlow() init { component.inject(this) @@ -36,43 +38,48 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { fun startRestoring() = launchOnIo { baseNodeIterator = baseNodesManager.baseNodeList.iterator() - tryNextBaseNode() + connectToNextBaseNode() } - private fun tryNextBaseNode() = launchOnIo { - logger.i("set next base node ${baseNodeIterator.hasNext()}") + private suspend fun connectToNextBaseNode() = withContext(Dispatchers.IO) { if (baseNodeIterator.hasNext()) { - startRestoringOnNode(baseNodeIterator.next()) + val nextBaseNode = baseNodeIterator.next() + logger.i("Trying to start restoring on base node ${nextBaseNode.publicKeyHex}") + startRestoringOnNode(nextBaseNode) } else { - onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed)) + logger.i("No more base nodes to try") + onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) } } - private fun startRestoringOnNode(baseNode: BaseNodeDto) { + private suspend fun startRestoringOnNode(baseNode: BaseNodeDto) { try { - val result = walletManager.startRecovery(baseNode, resourceManager.getString(R.string.restore_wallet_output_message)) - if (result) { + val startedSuccessfully = walletManager.startRecovery(baseNode, resourceManager.getString(R.string.restore_wallet_output_message)) + if (startedSuccessfully) { subscribeOnRestorationState() return } else { - tryNextBaseNode() + connectToNextBaseNode() } - onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed)) + onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) } catch (e: Throwable) { - onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed)) + onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) } } private fun subscribeOnRestorationState() { collectFlow(walletRestorationStateHandler.walletRestorationState) { state -> when (state) { - is WalletRestorationState.ConnectingToBaseNode -> onProgress(RecoveryState.ConnectingToBaseNode(resourceManager)) - is WalletRestorationState.ConnectedToBaseNode -> onProgress(RecoveryState.ConnectedToBaseNode(resourceManager)) + is WalletRestorationState.ConnectingToBaseNode -> updateState(RestorationState.ConnectingToBaseNode(resourceManager)) + is WalletRestorationState.ConnectedToBaseNode -> updateState(RestorationState.ConnectedToBaseNode(resourceManager)) is WalletRestorationState.ScanningRoundFailed -> onConnectionFailed(state.retryCount, state.retryLimit) is WalletRestorationState.ConnectionToBaseNodeFailed -> onConnectionFailed(state.retryCount, state.retryLimit) - is WalletRestorationState.Progress -> onProgress(RecoveryState.Recovery(resourceManager, state.currentBlock, state.numberOfBlocks)) + is WalletRestorationState.Progress -> updateState( + RestorationState.Recovery(resourceManager, state.currentBlock, state.numberOfBlocks) + ) + is WalletRestorationState.RecoveryFailed -> onError( - RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringFromSeedWordsViewModel::onErrorClosed) + RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::onErrorClosed) ) is WalletRestorationState.Completed -> onSuccessRestoration() @@ -83,11 +90,9 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private fun onConnectionFailed(retryCount: Long, retryLimit: Long) { if (retryCount == retryLimit) { - compositeDisposable.dispose() - compositeDisposable = CompositeDisposable() - tryNextBaseNode() + launchOnIo { connectToNextBaseNode() } } else { - onProgress(RecoveryState.ConnectionFailed(resourceManager, retryCount, retryLimit)) + updateState(RestorationState.ConnectionFailed(resourceManager, retryCount, retryLimit)) } } @@ -100,48 +105,49 @@ class WalletRestoringFromSeedWordsViewModel : CommonViewModel() { private fun onSuccessRestoration() { tariSettingsSharedRepository.hasVerifiedSeedWords = true - navigation.postValue(Navigation.WalletRestoringFromSeedWordsNavigation.OnRestoreCompleted) + walletManager.onWalletRestored() + tariNavigator.navigate(Navigation.SplashScreen(clearTop = false)) } - private fun onProgress(recoveryState: RecoveryState) = _recoveryState.postValue(recoveryState) + private fun updateState(recoveryState: RestorationState) = _recoveryState.update { recoveryState } - private fun onErrorClosed() = navigation.postValue(Navigation.WalletRestoringFromSeedWordsNavigation.OnRestoreFailed) + private fun onErrorClosed() = tariNavigator.onBackPressed() sealed class RestorationError(title: String, message: String, dismissAction: () -> Unit) { val args = SimpleDialogArgs(title = title, description = message, onClose = dismissAction) class ConnectionFailed(resourceManager: ResourceManager, dismissAction: () -> Unit) : RestorationError( - resourceManager.getString(R.string.restore_from_seed_words_overlay_error_title), - resourceManager.getString(R.string.restore_from_seed_words_overlay_error_description_connection_failed), - dismissAction + title = resourceManager.getString(R.string.restore_from_seed_words_overlay_error_title), + message = resourceManager.getString(R.string.restore_from_seed_words_overlay_error_description_connection_failed), + dismissAction = dismissAction, ) class RecoveryInternalError(resourceManager: ResourceManager, dismissAction: () -> Unit) : RestorationError( - resourceManager.getString(R.string.restore_from_seed_words_overlay_error_title), - resourceManager.getString(R.string.restore_from_seed_words_overlay_error_description_internal_error), - dismissAction + title = resourceManager.getString(R.string.restore_from_seed_words_overlay_error_title), + message = resourceManager.getString(R.string.restore_from_seed_words_overlay_error_description_internal_error), + dismissAction = dismissAction, ) } - sealed class RecoveryState(val status: String = "", val progress: String = "") { + sealed class RestorationState(val status: String = "", val progress: String = "") { - class ConnectingToBaseNode(resourceManager: ResourceManager) : RecoveryState( - resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connecting) + class ConnectingToBaseNode(resourceManager: ResourceManager) : RestorationState( + status = resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connecting) ) - class ConnectionFailed(resourceManager: ResourceManager, attempt: Long, maxAttempts: Long) : RecoveryState( - resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connecting), - resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connection_failed, attempt + 1, maxAttempts + 1) + class ConnectionFailed(resourceManager: ResourceManager, attempt: Long, maxAttempts: Long) : RestorationState( + status = resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connecting), + progress = resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connection_failed, attempt + 1, maxAttempts + 1), ) - class ConnectedToBaseNode(resourceManager: ResourceManager) : RecoveryState( - resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connected) + class ConnectedToBaseNode(resourceManager: ResourceManager) : RestorationState( + status = resourceManager.getString(R.string.restore_from_seed_words_overlay_status_connected) ) - class Recovery(resourceManager: ResourceManager, currentBlocks: Long, allBlocks: Long) : RecoveryState( - resourceManager.getString(R.string.restore_from_seed_words_overlay_status_progress), - DecimalFormat("#.##").format((currentBlocks.toDouble() / allBlocks) * 100) + "%" + class Recovery(resourceManager: ResourceManager, currentBlocks: Long, allBlocks: Long) : RestorationState( + status = resourceManager.getString(R.string.restore_from_seed_words_overlay_status_progress), + progress = DecimalFormat("#.##").format((currentBlocks.toDouble() / allBlocks) * 100) + "%", ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backgroundService/BackgroundServiceSettingsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backgroundService/BackgroundServiceSettingsViewModel.kt index 053e5aa11..b52c40c43 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backgroundService/BackgroundServiceSettingsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backgroundService/BackgroundServiceSettingsViewModel.kt @@ -44,6 +44,6 @@ class BackgroundServiceSettingsViewModel : CommonViewModel() { tariSettingsSharedRepository.backgroundServiceTurnedOn = isTurnedOn _switchState.value = TariLoadingSwitchState(isTurnedOn, false) hideDialog() - serviceLauncher.startIfExist() + serviceLauncher.startIfWalletExists() } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet_restoring_from_seed_words.xml b/app/src/main/res/layout/fragment_wallet_restoring.xml similarity index 100% rename from app/src/main/res/layout/fragment_wallet_restoring_from_seed_words.xml rename to app/src/main/res/layout/fragment_wallet_restoring.xml From f8d5ac8514b56fc8e390966d9ba1e7f635f410aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Wed, 9 Oct 2024 17:22:45 +0200 Subject: [PATCH 20/32] Add a possibility to cancel wallet restoration flow --- .../wallet/ui/common/CommonFragment.kt | 11 +++--- .../WalletRestoringFragment.kt | 6 ++-- .../WalletRestoringViewModel.kt | 35 ++++++++++++++++--- app/src/main/res/values/strings.xml | 7 ++-- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt index fa0619bbc..f94afe134 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt @@ -11,6 +11,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment @@ -141,10 +142,12 @@ abstract class CommonFragment : Fra } } - protected fun changeOnBackPressed(isBlocked: Boolean) { - blockingBackPressDispatcher.isEnabled = false - blockingBackPressDispatcher = MutedBackPressedCallback(isBlocked) - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, blockingBackPressDispatcher) + protected fun doOnBackPressed(onBackPressedAction: () -> Unit) { + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + onBackPressedAction() + } + }) } private fun copy(clipboardArgs: ClipboardArgs) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt index aa6f96583..d235eee0b 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt @@ -41,15 +41,13 @@ import com.tari.android.wallet.databinding.FragmentWalletRestoringBinding import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.ui.common.CommonFragment -class WalletRestoringFragment : - CommonFragment() { +class WalletRestoringFragment : CommonFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = FragmentWalletRestoringBinding.inflate(inflater, container, false).also { ui = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - changeOnBackPressed(true) val viewModel: WalletRestoringViewModel by viewModels() bindViewModel(viewModel) @@ -57,6 +55,8 @@ class WalletRestoringFragment : subscribeUI() viewModel.startRestoring() + + doOnBackPressed { viewModel.showResetFlowDialog() } } private fun subscribeUI() = with(viewModel) { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt index 8c47d87a7..1075edc13 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt @@ -1,5 +1,6 @@ package com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords +import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto @@ -10,11 +11,16 @@ import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs +import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule +import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle +import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.text.DecimalFormat import javax.inject.Inject @@ -41,6 +47,22 @@ class WalletRestoringViewModel : CommonViewModel() { connectToNextBaseNode() } + fun showResetFlowDialog() { + showModularDialog( + HeadModule(resourceManager.getString(R.string.restore_from_seed_words_cancel_dialog_title)), + BodyModule(resourceManager.getString(R.string.restore_from_seed_words_cancel_dialog_description)), + ButtonModule(resourceManager.getString(R.string.common_confirm), ButtonStyle.Warning) { + viewModelScope.launch(Dispatchers.Main) { + cancelRecovery() + hideDialog() + } + }, + ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close) { + hideDialog() + }, + ) + } + private suspend fun connectToNextBaseNode() = withContext(Dispatchers.IO) { if (baseNodeIterator.hasNext()) { val nextBaseNode = baseNodeIterator.next() @@ -48,7 +70,7 @@ class WalletRestoringViewModel : CommonViewModel() { startRestoringOnNode(nextBaseNode) } else { logger.i("No more base nodes to try") - onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) + onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } } @@ -61,9 +83,9 @@ class WalletRestoringViewModel : CommonViewModel() { } else { connectToNextBaseNode() } - onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) + onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } catch (e: Throwable) { - onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::onErrorClosed)) + onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } } @@ -79,7 +101,7 @@ class WalletRestoringViewModel : CommonViewModel() { ) is WalletRestorationState.RecoveryFailed -> onError( - RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::onErrorClosed) + RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::cancelRecovery) ) is WalletRestorationState.Completed -> onSuccessRestoration() @@ -111,7 +133,10 @@ class WalletRestoringViewModel : CommonViewModel() { private fun updateState(recoveryState: RestorationState) = _recoveryState.update { recoveryState } - private fun onErrorClosed() = tariNavigator.onBackPressed() + private fun cancelRecovery() { + walletManager.deleteWallet() + tariNavigator.navigate(Navigation.SplashScreen()) + } sealed class RestorationError(title: String, message: String, dismissAction: () -> Unit) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0b880d1cd..6e530f107 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -594,24 +594,21 @@ Select Custom Base Node Edit Base Node Oops, looks like the address you entered isn\'t good. We had to revert your change. - At least one of the entered seed words is invalid. Please review them and try again You entered not enough seed words. Please add some more and try again Unable to generate key from seed words. Please check your seed words and try again - This could take between 1 to 3 minutes.\nPlease keep the app open - Waiting for connection Connection established. The process will start soon Restoring in progress: Attempt %s out of %s - Recovery Failed Unable to connect with base node. Please try again later Unable to recover your wallet due to the error - Start typing to see suggestions here are no suggestions for the phrase + Are you sure you want to cancel? + You will lose your progress, but don\'t worry, you can always start over. Enter your backup password From 031121f3345bcbada793e191fcd88542c4b382c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 10 Oct 2024 13:58:21 +0200 Subject: [PATCH 21/32] Skip input seed words screen on restoring from paper wallet --- .../backup/BackupPrefRepository.kt | 29 +++---- .../infrastructure/backup/BackupManager.kt | 18 ++--- .../infrastructure/backup/BackupsState.kt | 4 +- .../ui/fragment/home/navigation/Navigation.kt | 2 +- .../fragment/home/navigation/TariNavigator.kt | 4 +- .../ChooseRestoreOptionFragment.kt | 39 ++++++---- .../ChooseRestoreOptionModel.kt | 15 ++++ .../ChooseRestoreOptionState.kt | 9 --- .../ChooseRestoreOptionViewModel.kt | 76 ++++++++++--------- .../option/RecoveryOptionView.kt | 10 +-- .../option/RecoveryOptionViewModel.kt | 4 +- .../inputSeedWords/InputSeedWordsFragment.kt | 6 +- .../inputSeedWords/InputSeedWordsViewModel.kt | 14 +--- .../WalletRestoringViewModel.kt | 32 ++++---- .../option/BackupOptionViewModel.kt | 16 ++-- .../{BackupOptions.kt => BackupOption.kt} | 2 +- .../settings/backup/data/BackupOptionDto.kt | 2 +- .../layout/fragment_choose_restore_option.xml | 12 +++ 18 files changed, 156 insertions(+), 138 deletions(-) create mode 100644 app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionModel.kt delete mode 100644 app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionState.kt rename app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/{BackupOptions.kt => BackupOption.kt} (79%) diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt index 6cdf7f8a6..6ee9133e3 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/backup/BackupPrefRepository.kt @@ -12,8 +12,8 @@ import com.tari.android.wallet.data.sharedPrefs.delegates.SharedPrefStringSecure import com.tari.android.wallet.data.sharedPrefs.network.NetworkPrefRepository import com.tari.android.wallet.data.sharedPrefs.network.formatKey import com.tari.android.wallet.infrastructure.backup.BackupUtxos +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions import com.tari.android.wallet.util.Constants import javax.inject.Inject import javax.inject.Singleton @@ -30,7 +30,7 @@ class BackupPrefRepository @Inject constructor( commonRepository = this, name = formatKey(Keys.LOCAL_FILE_OPTIONS_KEY), type = BackupOptionDto::class.java, - defValue = BackupOptionDto(BackupOptions.Local), + defValue = BackupOptionDto(BackupOption.Local), ) private var googleDriveOption: BackupOptionDto by SharedPrefGsonDelegate( @@ -38,14 +38,15 @@ class BackupPrefRepository @Inject constructor( commonRepository = this, name = formatKey(Keys.GOOGLE_DRIVE_OPTION_KEY), type = BackupOptionDto::class.java, - defValue = BackupOptionDto(BackupOptions.Google), + defValue = BackupOptionDto(BackupOption.Google), ) - var dropboxOption: BackupOptionDto? by SharedPrefGsonNullableDelegate( + var dropboxOption: BackupOptionDto by SharedPrefGsonDelegate( prefs = sharedPrefs, commonRepository = this, name = formatKey(Keys.DROPBOX_OPTIONS_KEY), type = BackupOptionDto::class.java, + defValue = BackupOptionDto(BackupOption.Dropbox), ) var dropboxCredential: DbxCredential? by SharedPrefGsonNullableDelegate( @@ -86,23 +87,23 @@ class BackupPrefRepository @Inject constructor( fun clear() { backupPassword = null localBackupFolderURI = null - localFileOption = BackupOptionDto(BackupOptions.Local) - googleDriveOption = BackupOptionDto(BackupOptions.Google) - dropboxOption = BackupOptionDto(BackupOptions.Dropbox) + localFileOption = BackupOptionDto(BackupOption.Local) + googleDriveOption = BackupOptionDto(BackupOption.Google) + dropboxOption = BackupOptionDto(BackupOption.Dropbox) } fun updateOption(option: BackupOptionDto) { when (option.type) { - BackupOptions.Google -> googleDriveOption = option - BackupOptions.Local -> localFileOption = option - BackupOptions.Dropbox -> dropboxOption = option + BackupOption.Google -> googleDriveOption = option + BackupOption.Local -> localFileOption = option + BackupOption.Dropbox -> dropboxOption = option } } - fun getOptionDto(type: BackupOptions): BackupOptionDto? = when (type) { - BackupOptions.Google -> googleDriveOption - BackupOptions.Local -> localFileOption - BackupOptions.Dropbox -> dropboxOption + fun getOptionDto(type: BackupOption): BackupOptionDto = when (type) { + BackupOption.Google -> googleDriveOption + BackupOption.Local -> localFileOption + BackupOption.Dropbox -> dropboxOption } companion object { diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt index f459330c2..2149449ee 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupManager.kt @@ -49,7 +49,7 @@ import com.tari.android.wallet.infrastructure.backup.googleDrive.GoogleDriveBack import com.tari.android.wallet.infrastructure.backup.local.LocalBackupStorage import com.tari.android.wallet.notification.NotificationHelper import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption import io.reactivex.subjects.BehaviorSubject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -76,7 +76,7 @@ class BackupManager @Inject constructor( private val logger get() = Logger.t(BackupManager::class.simpleName) - var currentOption: BackupOptions? = BackupOptions.Dropbox + var currentOption: BackupOption? = BackupOption.Dropbox private val backupMutex = Mutex() @@ -113,7 +113,7 @@ class BackupManager @Inject constructor( } } - fun setupStorage(option: BackupOptions, hostFragment: Fragment) { + fun setupStorage(option: BackupOption, hostFragment: Fragment) { currentOption = option getStorageByOption(option).setup(hostFragment) } @@ -125,7 +125,7 @@ class BackupManager @Inject constructor( private suspend fun backupAll() = backupSettingsRepository.getOptionList.forEach { backup(it.type) } - private suspend fun backup(optionType: BackupOptions) = backupMutex.withLock { + private suspend fun backup(optionType: BackupOption) = backupMutex.withLock { val currentDto = backupSettingsRepository.getOptionList.firstOrNull { it.type == optionType } ?: return if (!currentDto.isEnable) { logger.d("Backup is disabled. Exit.") @@ -173,7 +173,7 @@ class BackupManager @Inject constructor( backupSettingsRepository.getOptionList.forEach { turnOff(it.type) } } - fun turnOff(optionType: BackupOptions) = with(backupMutex) { + fun turnOff(optionType: BackupOption) = with(backupMutex) { val backupsState = EventBus.backupState.publishSubject.value!!.copy() backupSettingsRepository.updateOption(BackupOptionDto(optionType)) backupSettingsRepository.backupPassword = null @@ -198,10 +198,10 @@ class BackupManager @Inject constructor( else -> BackupState.BackupUpToDate } - private fun getStorageByOption(optionType: BackupOptions): BackupStorage = when (optionType) { - BackupOptions.Google -> googleDriveBackupStorage - BackupOptions.Local -> localFileBackupStorage - BackupOptions.Dropbox -> dropboxBackupStorage + private fun getStorageByOption(optionType: BackupOption): BackupStorage = when (optionType) { + BackupOption.Google -> googleDriveBackupStorage + BackupOption.Local -> localFileBackupStorage + BackupOption.Dropbox -> dropboxBackupStorage } private fun postBackupFailedNotification(exception: Exception) { diff --git a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupsState.kt b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupsState.kt index 96e4d6886..8bb33d52d 100644 --- a/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupsState.kt +++ b/app/src/main/java/com/tari/android/wallet/infrastructure/backup/BackupsState.kt @@ -1,8 +1,8 @@ package com.tari.android.wallet.infrastructure.backup -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption -data class BackupsState(val backupsStates: Map) { +data class BackupsState(val backupsStates: Map) { val backupsState: BackupState get() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt index 7edb4e1c2..77e780762 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/Navigation.kt @@ -89,7 +89,7 @@ sealed class Navigation { sealed class ChooseRestoreOptionNavigation : Navigation() { object ToEnterRestorePassword : ChooseRestoreOptionNavigation() - data class ToRestoreWithRecoveryPhrase(val seedWords: List?) : ChooseRestoreOptionNavigation() + data object ToRestoreWithRecoveryPhrase : ChooseRestoreOptionNavigation() } sealed class ContactBookNavigation : Navigation() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 4b2b46e26..5574cfc9b 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -121,7 +121,7 @@ class TariNavigator @Inject constructor( is ContactBookNavigation.ToAddPhoneContact -> toAddPhoneContact() is ContactBookNavigation.ToSelectTariUser -> addFragment(SelectUserContactFragment.newInstance()) is ChooseRestoreOptionNavigation.ToEnterRestorePassword -> toEnterRestorePassword() - is ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase -> toRestoreWithRecoveryPhrase(navigation.seedWords) + is ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase -> addFragment(InputSeedWordsFragment.createFragment()) is AllSettingsNavigation.ToBugReporting -> DebugActivity.launch(activity, DebugNavigation.BugReport) is AllSettingsNavigation.ToMyProfile -> toMyProfile() is AllSettingsNavigation.ToAbout -> toAbout() @@ -180,8 +180,6 @@ class TariNavigator @Inject constructor( private fun toEnterRestorePassword() = addFragment(EnterRestorationPasswordFragment.newInstance()) - private fun toRestoreWithRecoveryPhrase(seedWords: List?) = addFragment(InputSeedWordsFragment.createFragment(seedWords)) - fun onBackPressed() = activity.onBackPressed() fun toTxDetails(tx: Tx? = null, txId: TxId? = null) = activity.addFragment(TxDetailsFragment().apply { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt index 96b9d2147..82f9501f7 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionFragment.kt @@ -42,13 +42,15 @@ import androidx.core.view.children import androidx.fragment.app.viewModels import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.databinding.FragmentChooseRestoreOptionBinding -import com.tari.android.wallet.extension.observe +import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.ui.common.CommonFragment +import com.tari.android.wallet.ui.extension.gone import com.tari.android.wallet.ui.extension.parcelable +import com.tari.android.wallet.ui.extension.visible import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.option.RecoveryOptionView +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions class ChooseRestoreOptionFragment : CommonFragment() { @@ -88,18 +90,30 @@ class ChooseRestoreOptionFragment : CommonFragment + initOptions(uiState.backupOptions) + uiState.selectedOption?.let { updateProgress(it, uiState.isStarted) } + + if (uiState.paperWalletProgress) { + ui.restoreWithPaperWalletCtaView.isEnabled = false + ui.restoreWithPaperWalletProgressView.visible() + ui.restoreWithPaperWalletArrow.gone() + } else { + ui.restoreWithPaperWalletCtaView.isEnabled = true + ui.restoreWithPaperWalletProgressView.gone() + ui.restoreWithPaperWalletArrow.visible() + } + } } private fun initOptions(options: List) { + ui.optionsContainer.removeAllViews() for (option in options) { val view = RecoveryOptionView(requireContext()).apply { viewLifecycle = viewLifecycleOwner ui.restoreWalletCtaView.setOnClickListener { this@ChooseRestoreOptionFragment.viewModel.startRecovery( - options = option.type, + selectedOption = option.type, hostFragment = this@ChooseRestoreOptionFragment, ) } @@ -109,19 +123,12 @@ class ChooseRestoreOptionFragment : CommonFragment updateProgress(state.backupOptions, true) - is ChooseRestoreOptionState.EndProgress -> updateProgress(state.backupOptions, false) - } - } - - private fun updateProgress(backupOptions: BackupOptions, isStarted: Boolean) { + private fun updateProgress(backupOption: BackupOption, isStarted: Boolean) { blockingBackPressDispatcher.isEnabled = isStarted - getBackupOptionView(backupOptions)?.updateLoading(isStarted) + getBackupOptionView(backupOption)?.updateLoading(isStarted) } - private fun getBackupOptionView(backupOptions: BackupOptions): RecoveryOptionView? = + private fun getBackupOptionView(backupOptions: BackupOption): RecoveryOptionView? = ui.optionsContainer.children.mapNotNull { it as? RecoveryOptionView }.firstOrNull { it.viewModel.option == backupOptions } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionModel.kt new file mode 100644 index 000000000..44014ab70 --- /dev/null +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionModel.kt @@ -0,0 +1,15 @@ +package com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption + +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto + +object ChooseRestoreOptionModel { + data class UiState( + val backupOptions: List = emptyList(), + + val selectedOption: BackupOption? = null, + val isStarted: Boolean = false, + + val paperWalletProgress: Boolean = false, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionState.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionState.kt deleted file mode 100644 index 781b9597a..000000000 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionState.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption - -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions - -sealed class ChooseRestoreOptionState(val backupOptions: BackupOptions) { - class BeginProgress(backupOptions: BackupOptions) : ChooseRestoreOptionState(backupOptions) - - class EndProgress(backupOptions: BackupOptions) : ChooseRestoreOptionState(backupOptions) -} \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index b7165ef8f..9a999faa2 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -2,8 +2,6 @@ package com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption import android.content.Intent import androidx.fragment.app.Fragment -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import com.tari.android.wallet.R import com.tari.android.wallet.application.deeplinks.DeepLink import com.tari.android.wallet.application.walletManager.WalletFileUtil @@ -23,7 +21,6 @@ import com.tari.android.wallet.model.WalletError import com.tari.android.wallet.model.throwIf import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.common.SingleLiveEvent import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle @@ -31,8 +28,10 @@ import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import java.io.IOException import javax.inject.Inject @@ -50,35 +49,33 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { @Inject lateinit var walletConfig: WalletConfig - private val _state = SingleLiveEvent() - val state: LiveData = _state - - val options = MutableLiveData>() - init { component.inject(this) + } - options.postValue(backupPrefRepository.getOptionList) + private val _uiState = MutableStateFlow(ChooseRestoreOptionModel.UiState(backupOptions = backupPrefRepository.getOptionList)) + val uiState = _uiState.asStateFlow() + init { launchOnIo { walletManager.doOnWalletRunning { - if (WalletFileUtil.walletExists(walletConfig) && state.value != null) { - backupPrefRepository.restoredTxs?.let { - if (it.utxos.isEmpty()) return@let - - val tariWalletAddress = TariWalletAddress.fromBase58(it.sourceBase58) - val message = resourceManager.getString(R.string.backup_restored_tx) - val error = WalletError() - walletService.restoreWithUnbindedOutputs(it.utxos, tariWalletAddress, message, error) - throwIf(error) + uiState.value.selectedOption?.let { selectedOption -> + if (WalletFileUtil.walletExists(walletConfig)) { + backupPrefRepository.restoredTxs?.takeIf { it.utxos.isNotEmpty() }?.let { restoredTxs -> + val tariWalletAddress = TariWalletAddress.fromBase58(restoredTxs.sourceBase58) + val message = resourceManager.getString(R.string.backup_restored_tx) + val error = WalletError() + walletService.restoreWithUnbindedOutputs(restoredTxs.utxos, tariWalletAddress, message, error) + throwIf(error) + } + + val dto = backupPrefRepository.getOptionDto(selectedOption).copy(isEnable = true) + backupPrefRepository.updateOption(dto) + backupManager.backupNow() + + walletManager.onWalletRestored() + tariNavigator.navigate(Navigation.SplashScreen(clearTop = false)) } - - val dto = backupPrefRepository.getOptionDto(state.value!!.backupOptions)!!.copy(isEnable = true) - backupPrefRepository.updateOption(dto) - backupManager.backupNow() - - walletManager.onWalletRestored() - tariNavigator.navigate(Navigation.SplashScreen(clearTop = false)) } } } @@ -90,13 +87,13 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } } - fun startRecovery(options: BackupOptions, hostFragment: Fragment) { - _state.postValue(ChooseRestoreOptionState.BeginProgress(options)) - backupManager.setupStorage(options, hostFragment) + fun startRecovery(selectedOption: BackupOption, hostFragment: Fragment) { + _uiState.update { it.copy(isStarted = true) } + backupManager.setupStorage(selectedOption, hostFragment) } fun onRecoveryPhraseClicked() { - tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase(seedWords = null)) + tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase) } fun onPaperWalletClicked(fragment: Fragment) { @@ -120,7 +117,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } catch (exception: Exception) { logger.i(exception.message + "Backup storage setup failed") backupManager.signOut() - _state.postValue(ChooseRestoreOptionState.EndProgress(backupManager.currentOption!!)) + _uiState.update { it.copy(isStarted = false) } showAuthFailedDialog() } } @@ -138,6 +135,17 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } } + private fun restoreFromPaperWallet(seedWords: List) { + _uiState.update { it.copy(paperWalletProgress = true) } + walletServiceLauncher.start(seedWords) + launchOnIo { + walletManager.doOnWalletRunning { + _uiState.update { it.copy(paperWalletProgress = false) } + tariNavigator.navigate(Navigation.InputSeedWordsNavigation.ToRestoreFromSeeds) + } + } + } + private suspend fun handleException(exception: Throwable) { when (exception) { is BackupStorageAuthRevokedException -> { @@ -184,7 +192,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { } } - _state.postValue(ChooseRestoreOptionState.EndProgress(backupManager.currentOption!!)) + _uiState.update { it.copy(isStarted = false) } } private fun showPaperWalletDialog(seedWords: List) { @@ -193,7 +201,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_body)), ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_restore_button), ButtonStyle.Normal) { hideDialog() - tariNavigator.navigate(Navigation.ChooseRestoreOptionNavigation.ToRestoreWithRecoveryPhrase(seedWords)) + restoreFromPaperWallet(seedWords) }, ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_do_not_restore_button), ButtonStyle.Close), ) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionView.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionView.kt index 3f824f80d..ff2b8b50b 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionView.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionView.kt @@ -9,7 +9,7 @@ import com.tari.android.wallet.databinding.ViewRestoreOptionBinding import com.tari.android.wallet.ui.component.common.CommonView import com.tari.android.wallet.ui.extension.gone import com.tari.android.wallet.ui.extension.setVisible -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption class RecoveryOptionView : CommonView { @@ -26,11 +26,11 @@ class RecoveryOptionView : CommonView R.string.back_up_wallet_restore_with_google_drive - BackupOptions.Local -> R.string.back_up_wallet_restore_with_local_files - BackupOptions.Dropbox -> R.string.back_up_wallet_restore_with_dropbox + BackupOption.Google -> R.string.back_up_wallet_restore_with_google_drive + BackupOption.Local -> R.string.back_up_wallet_restore_with_local_files + BackupOption.Dropbox -> R.string.back_up_wallet_restore_with_dropbox } ui.title.text = context.getString(text) bindViewModel(RecoveryOptionViewModel().apply { this.option = option }) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionViewModel.kt index 05d4517d0..b4f1465ab 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/option/RecoveryOptionViewModel.kt @@ -1,9 +1,9 @@ package com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.option import com.tari.android.wallet.ui.common.CommonViewModel -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption class RecoveryOptionViewModel : CommonViewModel() { - var option: BackupOptions? = null + var option: BackupOption? = null } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt index e8e0a3bf0..37853bc6d 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt @@ -212,10 +212,6 @@ class InputSeedWordsFragment : CommonFragment?): InputSeedWordsFragment = InputSeedWordsFragment().apply { - arguments = Bundle().apply { putStringArray(PARAMETER_SEED_WORDS, seedWords?.toTypedArray()) } - } + fun createFragment(): InputSeedWordsFragment = InputSeedWordsFragment() } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt index 69b64ce33..2e6dfc1db 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsViewModel.kt @@ -3,7 +3,6 @@ package com.tari.android.wallet.ui.fragment.restore.inputSeedWords import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.map import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager @@ -25,7 +24,6 @@ import com.tari.android.wallet.ui.dialog.modular.SimpleDialogArgs import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.dialog.modular.modules.input.InputModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation -import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsFragment.Companion.PARAMETER_SEED_WORDS import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.suggestions.SuggestionState import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.suggestions.SuggestionViewHolderItem import io.reactivex.disposables.CompositeDisposable @@ -35,7 +33,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import javax.inject.Inject -class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() { +class InputSeedWordsViewModel() : CommonViewModel() { private var mnemonicList = mutableListOf() @@ -92,16 +90,6 @@ class InputSeedWordsViewModel(savedState: SavedStateHandle) : CommonViewModel() _suggestions.addSource(focusedIndex) { processSuggestions() } _suggestions.addSource(_words) { processSuggestions() } - - // Check if there are seed words for paper wallet restoration - val seedWords = savedState.get>(PARAMETER_SEED_WORDS) - if (seedWords != null) { - for (index in seedWords.indices) { - addWord(index, seedWords[index]) - } - finishEntering() - startRestoringWallet() - } } fun startRestoringWallet() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt index 1075edc13..2b754a3b3 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt @@ -6,6 +6,7 @@ import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.launchOnIo +import com.tari.android.wallet.extension.launchOnMain import com.tari.android.wallet.recovery.WalletRestorationState import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.ui.common.CommonViewModel @@ -91,25 +92,26 @@ class WalletRestoringViewModel : CommonViewModel() { private fun subscribeOnRestorationState() { collectFlow(walletRestorationStateHandler.walletRestorationState) { state -> - when (state) { - is WalletRestorationState.ConnectingToBaseNode -> updateState(RestorationState.ConnectingToBaseNode(resourceManager)) - is WalletRestorationState.ConnectedToBaseNode -> updateState(RestorationState.ConnectedToBaseNode(resourceManager)) - is WalletRestorationState.ScanningRoundFailed -> onConnectionFailed(state.retryCount, state.retryLimit) - is WalletRestorationState.ConnectionToBaseNodeFailed -> onConnectionFailed(state.retryCount, state.retryLimit) - is WalletRestorationState.Progress -> updateState( - RestorationState.Recovery(resourceManager, state.currentBlock, state.numberOfBlocks) - ) - - is WalletRestorationState.RecoveryFailed -> onError( - RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::cancelRecovery) - ) - - is WalletRestorationState.Completed -> onSuccessRestoration() + launchOnMain { + when (state) { + is WalletRestorationState.ConnectingToBaseNode -> updateState(RestorationState.ConnectingToBaseNode(resourceManager)) + is WalletRestorationState.ConnectedToBaseNode -> updateState(RestorationState.ConnectedToBaseNode(resourceManager)) + is WalletRestorationState.ScanningRoundFailed -> onConnectionFailed(state.retryCount, state.retryLimit) + is WalletRestorationState.ConnectionToBaseNodeFailed -> onConnectionFailed(state.retryCount, state.retryLimit) + is WalletRestorationState.Progress -> updateState( + RestorationState.Recovery(resourceManager, state.currentBlock, state.numberOfBlocks) + ) + + is WalletRestorationState.RecoveryFailed -> onError( + RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::cancelRecovery) + ) + + is WalletRestorationState.Completed -> onSuccessRestoration() + } } } } - private fun onConnectionFailed(retryCount: Long, retryLimit: Long) { if (retryCount == retryLimit) { launchOnIo { connectToNextBaseNode() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/backupSettings/option/BackupOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/backupSettings/option/BackupOptionViewModel.kt index 463da942c..539ed6604 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/backupSettings/option/BackupOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/backupSettings/option/BackupOptionViewModel.kt @@ -21,7 +21,7 @@ import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptionDto -import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOptions +import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupOption import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.joda.time.DateTime @@ -57,12 +57,12 @@ class BackupOptionViewModel : CommonViewModel() { val title: Int get() = when (option.value!!.type) { - BackupOptions.Google -> R.string.back_up_wallet_google_title - BackupOptions.Local -> R.string.back_up_wallet_local_file_title - BackupOptions.Dropbox -> R.string.back_up_wallet_dropbox_backup_title + BackupOption.Google -> R.string.back_up_wallet_google_title + BackupOption.Local -> R.string.back_up_wallet_local_file_title + BackupOption.Dropbox -> R.string.back_up_wallet_dropbox_backup_title } - fun setup(option: BackupOptions) { + fun setup(option: BackupOption) { _option.value = backupSettingsRepository.getOptionList.first { it.type == option } _switchChecked.value = _option.value!!.isEnable onBackupStateChanged(EventBus.backupState.publishSubject.value?.backupsStates?.get(option)) @@ -73,7 +73,7 @@ class BackupOptionViewModel : CommonViewModel() { val currentOption = _option.value!!.type try { if (backupManager.onSetupActivityResult(requestCode, resultCode, data)) { - backupSettingsRepository.getOptionDto(currentOption)?.copy(isEnable = true)?.let { backupSettingsRepository.updateOption(it) } + backupSettingsRepository.getOptionDto(currentOption).copy(isEnable = true).let { backupSettingsRepository.updateOption(it) } EventBus.backupState.publishSubject .filter { it.backupsStates[currentOption] is BackupState.BackupUpToDate || it.backupsStates[currentOption] is BackupState.BackupFailed @@ -89,7 +89,7 @@ class BackupOptionViewModel : CommonViewModel() { } } - private fun turnOff(backupOption: BackupOptions, throwable: Throwable?) { + private fun turnOff(backupOption: BackupOption, throwable: Throwable?) { logger.i("Backup storage setup failed: $throwable") backupManager.turnOff(backupOption) _inProgress.postValue(false) @@ -187,7 +187,7 @@ class BackupOptionViewModel : CommonViewModel() { val currentState = backupSettingsRepository.getOptionDto(_option.value!!.type) _inProgress.postValue(false) _switchChecked.postValue(true) - updateLastSuccessfulBackupDate(currentState?.lastSuccessDate?.date) + updateLastSuccessfulBackupDate(currentState.lastSuccessDate?.date) } private fun handleInProgressState() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptions.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOption.kt similarity index 79% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptions.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOption.kt index 5bde32dc9..11aaf46ab 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptions.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOption.kt @@ -1,6 +1,6 @@ package com.tari.android.wallet.ui.fragment.settings.backup.data -enum class BackupOptions { +enum class BackupOption { Google, Dropbox, Local diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptionDto.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptionDto.kt index ddeefe2c6..801730b5e 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptionDto.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/settings/backup/data/BackupOptionDto.kt @@ -4,7 +4,7 @@ import com.tari.android.wallet.data.sharedPrefs.delegates.SerializableTime import java.io.Serializable data class BackupOptionDto( - val type: BackupOptions, + val type: BackupOption, val isEnable: Boolean = false, val lastSuccessDate: SerializableTime? = null, val lastFailureDate: SerializableTime? = null diff --git a/app/src/main/res/layout/fragment_choose_restore_option.xml b/app/src/main/res/layout/fragment_choose_restore_option.xml index a6da02306..63455c715 100644 --- a/app/src/main/res/layout/fragment_choose_restore_option.xml +++ b/app/src/main/res/layout/fragment_choose_restore_option.xml @@ -1,6 +1,7 @@ @@ -8,6 +9,7 @@ + + Date: Thu, 10 Oct 2024 16:57:06 +0200 Subject: [PATCH 22/32] Add extensions for switching coroutine context --- .../wallet/extension/ViewModelExtension.kt | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/extension/ViewModelExtension.kt b/app/src/main/java/com/tari/android/wallet/extension/ViewModelExtension.kt index 771b8c058..ff67fa9a7 100644 --- a/app/src/main/java/com/tari/android/wallet/extension/ViewModelExtension.kt +++ b/app/src/main/java/com/tari/android/wallet/extension/ViewModelExtension.kt @@ -56,10 +56,6 @@ fun CommonView<*, *>.observe(liveData: LiveData, action: (data: T) -> Uni } } -fun CommonView<*, *>.observeOnLoad(liveData: LiveData) { - observe(liveData) { } -} - fun LiveData.debounce(duration: Long = 1000L) = MediatorLiveData().also { mld -> val source = this val handler = Handler(Looper.getMainLooper()) @@ -75,17 +71,17 @@ fun LiveData.debounce(duration: Long = 1000L) = MediatorLiveData().als } fun ViewModel.launchOnIo(action: suspend () -> Unit): Job { - return viewModelScope.launch { - withContext(Dispatchers.IO) { - action() - } + return viewModelScope.launch(Dispatchers.IO) { + action() } } fun ViewModel.launchOnMain(action: suspend () -> Unit): Job { - return viewModelScope.launch { - withContext(Dispatchers.Main) { - action() - } + return viewModelScope.launch(Dispatchers.Main) { + action() } -} \ No newline at end of file +} + +suspend fun switchToIo(action: suspend () -> Unit) = withContext(Dispatchers.IO) { action() } + +suspend fun switchToMain(action: suspend () -> Unit) = withContext(Dispatchers.Main) { action() } \ No newline at end of file From 5635a92779747d0f00917a3e901ed18e34834e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Thu, 10 Oct 2024 17:26:11 +0200 Subject: [PATCH 23/32] Start restore from paper wallet process if splash screen started with seeds arg --- .../application/walletManager/WalletManager.kt | 1 + .../onboarding/activity/OnboardingFlowActivity.kt | 14 ++++++++++++++ .../WalletRestoringViewModel.kt | 14 +++++++------- .../main/res/layout/fragment_wallet_restoring.xml | 11 ++++++----- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index ffd1c397b..b71ff0cc4 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -271,6 +271,7 @@ class WalletManager @Inject constructor( } fun deleteWallet() { + logger.i("Deleting wallet: ${walletInstance?.getWalletAddress()?.fullBase58() ?: "wallet is already null!"}") walletInstance?.destroy() walletInstance = null _walletState.update { WalletState.NotReady } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt index 0c0acd2e7..8e6af9419 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt @@ -42,6 +42,7 @@ import androidx.lifecycle.lifecycleScope import com.tari.android.wallet.R import com.tari.android.wallet.application.walletManager.WalletManager import com.tari.android.wallet.application.walletManager.doOnWalletFailed +import com.tari.android.wallet.application.walletManager.doOnWalletRunning import com.tari.android.wallet.data.sharedPrefs.CorePrefRepository import com.tari.android.wallet.databinding.ActivityOnboardingFlowBinding import com.tari.android.wallet.di.DiContainer.appComponent @@ -55,6 +56,7 @@ import com.tari.android.wallet.ui.fragment.onboarding.activity.OnboardingFlowMod import com.tari.android.wallet.ui.fragment.onboarding.createWallet.CreateWalletFragment import com.tari.android.wallet.ui.fragment.onboarding.inroduction.IntroductionFragment import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthFragment +import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFragment import com.tari.android.wallet.ui.fragment.settings.networkSelection.NetworkSelectionFragment import kotlinx.coroutines.launch import javax.inject.Inject @@ -90,7 +92,19 @@ class OnboardingFlowActivity : CommonActivity { + walletServiceLauncher.start(paperWalletSeeds) + + lifecycleScope.launch { + walletManager.doOnWalletRunning { + loadFragment(WalletRestoringFragment()) + } + } + } + corePrefRepository.onboardingAuthWasInterrupted -> { walletServiceLauncher.start() loadFragment(LocalAuthFragment()) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt index 2b754a3b3..651e8c7d6 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt @@ -7,6 +7,8 @@ import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeDto import com.tari.android.wallet.extension.collectFlow import com.tari.android.wallet.extension.launchOnIo import com.tari.android.wallet.extension.launchOnMain +import com.tari.android.wallet.extension.switchToIo +import com.tari.android.wallet.extension.switchToMain import com.tari.android.wallet.recovery.WalletRestorationState import com.tari.android.wallet.recovery.WalletRestorationStateHandler import com.tari.android.wallet.ui.common.CommonViewModel @@ -45,6 +47,7 @@ class WalletRestoringViewModel : CommonViewModel() { fun startRestoring() = launchOnIo { baseNodeIterator = baseNodesManager.baseNodeList.iterator() + subscribeOnRestorationState() connectToNextBaseNode() } @@ -78,13 +81,10 @@ class WalletRestoringViewModel : CommonViewModel() { private suspend fun startRestoringOnNode(baseNode: BaseNodeDto) { try { val startedSuccessfully = walletManager.startRecovery(baseNode, resourceManager.getString(R.string.restore_wallet_output_message)) - if (startedSuccessfully) { - subscribeOnRestorationState() - return - } else { + if (!startedSuccessfully) { connectToNextBaseNode() + onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } - onError(RestorationError.ConnectionFailed(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } catch (e: Throwable) { onError(RestorationError.RecoveryInternalError(resourceManager, this@WalletRestoringViewModel::cancelRecovery)) } @@ -112,9 +112,9 @@ class WalletRestoringViewModel : CommonViewModel() { } } - private fun onConnectionFailed(retryCount: Long, retryLimit: Long) { + private suspend fun onConnectionFailed(retryCount: Long, retryLimit: Long) = switchToMain { if (retryCount == retryLimit) { - launchOnIo { connectToNextBaseNode() } + switchToIo { connectToNextBaseNode() } } else { updateState(RestorationState.ConnectionFailed(resourceManager, retryCount, retryLimit)) } diff --git a/app/src/main/res/layout/fragment_wallet_restoring.xml b/app/src/main/res/layout/fragment_wallet_restoring.xml index e5fc5f4d6..bca109bfd 100644 --- a/app/src/main/res/layout/fragment_wallet_restoring.xml +++ b/app/src/main/res/layout/fragment_wallet_restoring.xml @@ -1,6 +1,7 @@ @@ -16,7 +17,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.4" /> + app:layout_constraintVertical_bias="0.4" /> + app:layout_constraintTop_toBottomOf="@id/description_view" + tools:text="Waiting for connection" /> + app:layout_constraintTop_toBottomOf="@id/status_label" + tools:text="Attempts 5 out of 10" /> Date: Thu, 10 Oct 2024 17:51:02 +0200 Subject: [PATCH 24/32] Fix activity context leak on showing dialogs --- .../application/deeplinks/DeeplinkManager.kt | 16 +++--- .../wallet/ui/common/CommonFragment.kt | 6 +-- .../android/wallet/ui/common/DialogManager.kt | 6 +-- .../wallet/ui/component/common/CommonView.kt | 3 +- .../ui/dialog/modular/InputModularDialog.kt | 6 +-- .../wallet/ui/dialog/modular/ModularDialog.kt | 54 +++++++++++-------- .../wallet/ui/extension/ContextExtensions.kt | 3 ++ .../wallet/ui/extension/ViewExtensions.kt | 3 +- .../ContactSelectionFragment.kt | 2 +- .../ContactSelectionViewModel.kt | 4 +- .../ui/fragment/qr/QrScannerViewModel.kt | 4 +- .../send/addAmount/AddAmountFragment.kt | 6 +-- .../send/requestTari/RequestTariFragment.kt | 2 +- .../ChangeSecurePasswordFragment.kt | 2 +- .../wallet/ui/fragment/tx/HomeFragment.kt | 2 +- .../ui/fragment/tx/HomeFragmentViewModel.kt | 4 +- .../fragment/tx/details/TxDetailsFragment.kt | 4 +- 17 files changed, 72 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index 39a34041e..f1e4ed455 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -1,6 +1,6 @@ package com.tari.android.wallet.application.deeplinks -import android.content.Context +import android.app.Activity import com.tari.android.wallet.R import com.tari.android.wallet.application.baseNodes.BaseNodesManager import com.tari.android.wallet.application.walletManager.WalletManager @@ -48,7 +48,7 @@ class DeeplinkManager @Inject constructor( /** * Executes the given deeplink, but first shows a confirmation dialog. */ - fun execute(context: Context, deeplink: DeepLink, isQrData: Boolean = true) { + fun execute(context: Activity, deeplink: DeepLink, isQrData: Boolean = true) { when (deeplink) { is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink, isQrData) is DeepLink.Contacts -> showAddContactsDialog(context, deeplink, isQrData) @@ -62,7 +62,7 @@ class DeeplinkManager @Inject constructor( /** * Executes the given deeplink without showing a confirmation dialog. */ - fun executeRawDeeplink(context: Context, deeplink: DeepLink, isQrData: Boolean = true) { + fun executeRawDeeplink(context: Activity, deeplink: DeepLink, isQrData: Boolean = true) { when (deeplink) { is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink) is DeepLink.Contacts -> addContactsAction(deeplink.data(), isQrData) @@ -73,7 +73,7 @@ class DeeplinkManager @Inject constructor( } } - private fun showAddBaseNodeDialog(context: Context, deeplink: DeepLink.AddBaseNode, isQrData: Boolean = true) { + private fun showAddBaseNodeDialog(context: Activity, deeplink: DeepLink.AddBaseNode, isQrData: Boolean = true) { val baseNode = deeplink.data() dialogManager.replace( context = context, @@ -91,7 +91,7 @@ class DeeplinkManager @Inject constructor( ) } - private fun addUserProfile(context: Context, deeplink: DeepLink.UserProfile, isQrData: Boolean) { + private fun addUserProfile(context: Activity, deeplink: DeepLink.UserProfile, isQrData: Boolean) { val contact = DeepLink.Contacts( listOf( DeepLink.Contacts.DeeplinkContact( @@ -103,7 +103,7 @@ class DeeplinkManager @Inject constructor( showAddContactsDialog(context, contact, isQrData) } - private fun showAddContactsDialog(context: Context, deeplink: DeepLink.Contacts, isQrData: Boolean = true) { + private fun showAddContactsDialog(context: Activity, deeplink: DeepLink.Contacts, isQrData: Boolean = true) { val contactDtos = deeplink.data() if (contactDtos.isEmpty()) return val names = contactDtos.joinToString(", ") { it.contactInfo.getAlias().trim() } @@ -130,7 +130,7 @@ class DeeplinkManager @Inject constructor( } } - private fun showPaperWalletDialog(context: Context, deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { + private fun showPaperWalletDialog(context: Activity, deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { dialogManager.replace( context = context, args = ModularDialogArgs( @@ -152,7 +152,7 @@ class DeeplinkManager @Inject constructor( ) } - private fun showRememberToBackupDialog(context: Context, deeplink: DeepLink.PaperWallet) { + private fun showRememberToBackupDialog(context: Activity, deeplink: DeepLink.PaperWallet) { dialogManager.replace( context = context, args = ModularDialogArgs( diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt index f94afe134..8c8fdadc9 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/CommonFragment.kt @@ -113,9 +113,9 @@ abstract class CommonFragment : Fra observe(copyToClipboard) { copy(it) } - observe(modularDialog) { dialogManager.replace(ModularDialog(requireContext(), it)) } + observe(modularDialog) { dialogManager.replace(ModularDialog(requireActivity(), it)) } - observe(inputDialog) { dialogManager.replace(InputModularDialog(requireContext(), it)) } + observe(inputDialog) { dialogManager.replace(InputModularDialog(requireActivity(), it)) } observe(showToast) { TariToast(requireContext(), it) } @@ -131,7 +131,7 @@ abstract class CommonFragment : Fra observe(permissionManager.openSettings) { openSettings() } - observe(permissionManager.dialog) { dialogManager.replace(ModularDialog(requireContext(), it)) } + observe(permissionManager.dialog) { dialogManager.replace(ModularDialog(requireActivity(), it)) } } private fun openSettings() { diff --git a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt index 10107e33c..ab17e0af1 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/common/DialogManager.kt @@ -1,6 +1,6 @@ package com.tari.android.wallet.ui.common -import android.content.Context +import android.app.Activity import com.tari.android.wallet.R import com.tari.android.wallet.ui.dialog.modular.ModularDialog import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs @@ -26,7 +26,7 @@ class DialogManager @Inject constructor() { } } - fun replace(context: Context, args: ModularDialogArgs) { + fun replace(context: Activity, args: ModularDialogArgs) { replace(ModularDialog(context, args)) } @@ -50,7 +50,7 @@ class DialogManager @Inject constructor() { fun isDialogShowing(dialogId: Int) = dialogId != DialogId.NO_ID && dialogQueue.any { it.args.dialogId == dialogId } - fun showNotReadyYetDialog(context: Context) { + fun showNotReadyYetDialog(context: Activity) { replace( context, ModularDialogArgs( modules = listOf( diff --git a/app/src/main/java/com/tari/android/wallet/ui/component/common/CommonView.kt b/app/src/main/java/com/tari/android/wallet/ui/component/common/CommonView.kt index 69eebc76c..3423bb921 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/component/common/CommonView.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/component/common/CommonView.kt @@ -13,6 +13,7 @@ import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.common.DialogManager import com.tari.android.wallet.ui.component.tari.toast.TariToast import com.tari.android.wallet.ui.dialog.modular.ModularDialog +import contacts.ui.view.activity abstract class CommonView : LinearLayout { @@ -58,7 +59,7 @@ abstract class CommonView : LinearLayout openLink.observe(viewLifecycle) { context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) } - modularDialog.observe(viewLifecycle) { dialogManager.replace(ModularDialog(context, it)) } + modularDialog.observe(viewLifecycle) { args -> activity?.let { activity -> dialogManager.replace(ModularDialog(activity, args)) } } showToast.observe(viewLifecycle) { TariToast(context, it) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/InputModularDialog.kt b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/InputModularDialog.kt index 0c2059f18..9c648f0d1 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/InputModularDialog.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/InputModularDialog.kt @@ -1,6 +1,6 @@ package com.tari.android.wallet.ui.dialog.modular -import android.content.Context +import android.app.Activity import android.view.ViewGroup import android.view.WindowManager import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE @@ -10,9 +10,9 @@ import com.tari.android.wallet.R import com.tari.android.wallet.ui.component.tari.background.obsolete.TariPrimaryBackgroundConstraint -class InputModularDialog(context: Context) : ModularDialog(context) { +class InputModularDialog(context: Activity) : ModularDialog(context) { - constructor(context: Context, args: ModularDialogArgs) : this(context) { + constructor(context: Activity, args: ModularDialogArgs) : this(context) { applyArgs(args) modifyDialog() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialog.kt b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialog.kt index e26bf7d5e..788dd833f 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialog.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialog.kt @@ -3,7 +3,6 @@ package com.tari.android.wallet.ui.dialog.modular import android.animation.ValueAnimator import android.app.Activity import android.app.Dialog -import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.view.Gravity @@ -48,6 +47,7 @@ import com.tari.android.wallet.ui.dialog.modular.modules.shortEmoji.ShortEmojiId import com.tari.android.wallet.ui.dialog.modular.modules.shortEmoji.ShortEmojiModuleView import com.tari.android.wallet.ui.dialog.modular.modules.space.SpaceModule import com.tari.android.wallet.ui.dialog.modular.modules.space.SpaceModuleView +import com.tari.android.wallet.ui.extension.isStillAlive import com.tari.android.wallet.ui.fragment.send.addAmount.feeModule.FeeModule import com.tari.android.wallet.ui.fragment.send.addAmount.feeModule.FeeModuleView import com.tari.android.wallet.ui.fragment.send.shareQr.ShareQRCodeModuleView @@ -64,14 +64,16 @@ import com.tari.android.wallet.ui.fragment.utxos.list.module.UtxoAmountModule import com.tari.android.wallet.ui.fragment.utxos.list.module.UtxoAmountModuleView import com.tari.android.wallet.ui.fragment.utxos.list.module.UtxoSplitModule import com.tari.android.wallet.ui.fragment.utxos.list.module.UtxoSplitModuleView +import java.lang.ref.WeakReference -open class ModularDialog(val context: Context) { +open class ModularDialog(context: Activity) { + private val weakContext = WeakReference(context) lateinit var args: ModularDialogArgs private val onDismissListeners = mutableListOf<() -> Unit>() - constructor(context: Context, args: ModularDialogArgs) : this(context) { + constructor(context: Activity, args: ModularDialogArgs) : this(context) { applyArgs(args) } @@ -85,14 +87,16 @@ open class ModularDialog(val context: Context) { } fun show() { - dialog.show() - showAnimation(true) + withContext { + dialog.show() + showAnimation(true) + } } fun dismiss() { - showAnimation(false) { - runCatching { - if (context !is Activity || !context.isFinishing) { + withContext { + showAnimation(false) { + runCatching { dialog.dismiss() } } @@ -104,24 +108,26 @@ open class ModularDialog(val context: Context) { } fun applyArgs(args: ModularDialogArgs) { - this.args = args - with(dialog) { - setCancelable(args.dialogArgs.cancelable) - setCanceledOnTouchOutside(args.dialogArgs.canceledOnTouchOutside) - setOnDismissListener { - onDismissListeners.forEach { runCatching { it() } } - args.dialogArgs.onDismiss() - } - if (args.dialogArgs.canceledOnTouchOutside) { - findViewById(R.id.back).setOnClickListener { - this@ModularDialog.dismiss() + withContext { context -> + this.args = args + with(dialog) { + setCancelable(args.dialogArgs.cancelable) + setCanceledOnTouchOutside(args.dialogArgs.canceledOnTouchOutside) + setOnDismissListener { + onDismissListeners.forEach { runCatching { it() } } + args.dialogArgs.onDismiss() + } + if (args.dialogArgs.canceledOnTouchOutside) { + findViewById(R.id.back).setOnClickListener { + this@ModularDialog.dismiss() + } } } + updateModules(context, args.modules) } - updateModules(args.modules) } - private fun updateModules(modules: List) { + private fun updateModules(context: Activity, modules: List) { val root = dialog.findViewById(R.id.dialog_root_view) root.removeAllViews() for (module in modules) { @@ -180,4 +186,10 @@ open class ModularDialog(val context: Context) { start() } } + + private fun withContext(block: (Activity) -> Unit) { + weakContext.get()?.takeIf { it.isStillAlive() }?.let { + block(it) + } + } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/extension/ContextExtensions.kt b/app/src/main/java/com/tari/android/wallet/ui/extension/ContextExtensions.kt index 239cbc0fe..bfaff855a 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/extension/ContextExtensions.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/extension/ContextExtensions.kt @@ -32,6 +32,7 @@ */ package com.tari.android.wallet.ui.extension +import android.app.Activity import android.content.ContentResolver import android.content.Context import android.graphics.drawable.Drawable @@ -67,3 +68,5 @@ fun Context.getResourceUri(resourceId: Int): Uri = Uri.Builder() .authority(packageName) .path(resourceId.toString()) .build() + +fun Context.isStillAlive(): Boolean = this is Activity && !this.isFinishing && !this.isDestroyed \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/ui/extension/ViewExtensions.kt b/app/src/main/java/com/tari/android/wallet/ui/extension/ViewExtensions.kt index e1b5c2511..ea3183971 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/extension/ViewExtensions.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/extension/ViewExtensions.kt @@ -34,6 +34,7 @@ package com.tari.android.wallet.ui.extension import android.animation.AnimatorSet import android.animation.ValueAnimator +import android.app.Activity import android.content.Context import android.content.res.TypedArray import android.graphics.drawable.Drawable @@ -89,7 +90,7 @@ fun View.setVisible(visible: Boolean, hideState: Int = View.GONE) { /** * Given the context, displays the standard "no internet connection" dialog. */ -fun showInternetConnectionErrorDialog(context: Context) { +fun showInternetConnectionErrorDialog(context: Activity) { val args = ModularDialogArgs( DialogArgs(), listOf( HeadModule(context.string(R.string.internet_connection_error_dialog_title)), diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt index b6810e0cc..a02d86b46 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/contactSelection/ContactSelectionFragment.kt @@ -339,7 +339,7 @@ open class ContactSelectionFragment : CommonFragment(EXTRA_QR_DATA_SOURCE) ?: QrScannerSource.None - fun onAlternativeApply(context: Context) { + fun onAlternativeApply(context: Activity) { backPressed.postValue(Unit) launchOnIo { delay(500) diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountFragment.kt index be5202453..8eefa0d58 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/send/addAmount/AddAmountFragment.kt @@ -192,7 +192,7 @@ class AddAmountFragment : CommonFragment( override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == QrScannerActivity.REQUEST_QR_SCANNER && resultCode == Activity.RESULT_OK && data != null) { val qrDeepLink = data.parcelable(QrScannerActivity.EXTRA_DEEPLINK) ?: return - viewModel.handleDeeplink(requireContext(), qrDeepLink) + viewModel.handleDeeplink(requireActivity(), qrDeepLink) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt index 86e37b1f7..2fc164372 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt @@ -1,7 +1,7 @@ package com.tari.android.wallet.ui.fragment.tx -import android.content.Context +import android.app.Activity import android.os.Build import android.text.SpannableString import android.text.Spanned @@ -131,7 +131,7 @@ class HomeFragmentViewModel : CommonViewModel() { } } - fun handleDeeplink(context: Context, deepLink: DeepLink) { + fun handleDeeplink(context: Activity, deepLink: DeepLink) { deeplinkManager.execute(context, deepLink) } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt index 7db831874..3c815abeb 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/details/TxDetailsFragment.kt @@ -40,6 +40,7 @@ import android.view.ViewGroup import androidx.fragment.app.viewModels import com.bumptech.glide.Glide import com.tari.android.wallet.R +import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.databinding.FragmentTxDetailsBinding import com.tari.android.wallet.extension.collectNonNullFlow import com.tari.android.wallet.extension.observe @@ -73,7 +74,6 @@ import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto import com.tari.android.wallet.ui.fragment.tx.details.gif.GifView import com.tari.android.wallet.ui.fragment.tx.details.gif.GifViewModel import com.tari.android.wallet.ui.fragment.tx.details.gif.TxState -import com.tari.android.wallet.application.walletManager.WalletFileUtil import com.tari.android.wallet.util.addressFirstEmojis import com.tari.android.wallet.util.addressLastEmojis import com.tari.android.wallet.util.addressPrefixEmojis @@ -271,7 +271,7 @@ class TxDetailsFragment : CommonFragment Date: Fri, 11 Oct 2024 14:26:49 +0200 Subject: [PATCH 25/32] Hide the sweep funds option by a feature flag --- .../android/wallet/application/deeplinks/DeeplinkManager.kt | 5 +++-- .../main/java/com/tari/android/wallet/util/DebugConfig.kt | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index f1e4ed455..85e1df036 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -22,6 +22,7 @@ import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.FFIContactInfo import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.home.navigation.TariNavigator +import com.tari.android.wallet.util.DebugConfig import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -135,13 +136,13 @@ class DeeplinkManager @Inject constructor( context = context, args = ModularDialogArgs( dialogId = DialogId.DEEPLINK_PAPER_WALLET, - modules = listOf( + modules = listOfNotNull( HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_title)), BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_body)), ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_sweep_funds_button), ButtonStyle.Normal) { dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET) dialogManager.showNotReadyYetDialog(context) - }, + }.takeIf { DebugConfig.sweepFundsButtonEnabled }, ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_replace_wallet_button), ButtonStyle.Normal) { dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET) showRememberToBackupDialog(context, deeplink) diff --git a/app/src/main/java/com/tari/android/wallet/util/DebugConfig.kt b/app/src/main/java/com/tari/android/wallet/util/DebugConfig.kt index 149dc79e8..650395b7c 100644 --- a/app/src/main/java/com/tari/android/wallet/util/DebugConfig.kt +++ b/app/src/main/java/com/tari/android/wallet/util/DebugConfig.kt @@ -91,6 +91,8 @@ object DebugConfig { val showCopySeedsButton = valueIfDebug(true) + val sweepFundsButtonEnabled = valueIfDebug(false) + private fun isDebug() = BuildConfig.BUILD_TYPE == "debug" private fun valueIfDebug(value: Boolean) = isDebug() && value From 883a7942a66723d799644f28053e8096b50095d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Fri, 11 Oct 2024 15:04:55 +0200 Subject: [PATCH 26/32] Add the wallet restoring success dialog on the home page --- .../application/deeplinks/DeeplinkManager.kt | 48 +++++++++---------- .../walletManager/WalletManager.kt | 1 + .../data/sharedPrefs/CorePrefRepository.kt | 3 ++ .../android/wallet/di/ApplicationComponent.kt | 2 +- .../contactBook/root/ShareViewModel.kt | 2 +- .../wallet/ui/fragment/home/HomeActivity.kt | 2 +- .../wallet/ui/fragment/home/HomeViewModel.kt | 4 -- .../fragment/home/navigation/TariNavigator.kt | 2 +- .../activity/OnboardingFlowActivity.kt | 2 +- .../WalletRestoringFragment.kt | 2 +- .../WalletRestoringViewModel.kt | 2 +- .../ui/fragment/tx/HomeFragmentViewModel.kt | 13 +++++ app/src/main/res/values/strings.xml | 3 ++ 13 files changed, 51 insertions(+), 35 deletions(-) rename app/src/main/java/com/tari/android/wallet/ui/fragment/restore/{walletRestoringFromSeedWords => walletRestoring}/WalletRestoringFragment.kt (99%) rename app/src/main/java/com/tari/android/wallet/ui/fragment/restore/{walletRestoringFromSeedWords => walletRestoring}/WalletRestoringViewModel.kt (99%) diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index 85e1df036..445e66d29 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -49,32 +49,32 @@ class DeeplinkManager @Inject constructor( /** * Executes the given deeplink, but first shows a confirmation dialog. */ - fun execute(context: Activity, deeplink: DeepLink, isQrData: Boolean = true) { + fun execute(context: Activity, deeplink: DeepLink) { when (deeplink) { - is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink, isQrData) - is DeepLink.Contacts -> showAddContactsDialog(context, deeplink, isQrData) - is DeepLink.Send -> sendAction(deeplink, isQrData) - is DeepLink.UserProfile -> addUserProfile(context, deeplink, isQrData) - is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink, isQrData) + is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink) + is DeepLink.Contacts -> showAddContactsDialog(context, deeplink) + is DeepLink.Send -> sendAction(deeplink) + is DeepLink.UserProfile -> addUserProfile(context, deeplink) + is DeepLink.TorBridges -> addTorBridges(deeplink) + is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink) } } /** * Executes the given deeplink without showing a confirmation dialog. */ - fun executeRawDeeplink(context: Activity, deeplink: DeepLink, isQrData: Boolean = true) { + fun executeRawDeeplink(context: Activity, deeplink: DeepLink) { when (deeplink) { is DeepLink.AddBaseNode -> showAddBaseNodeDialog(context, deeplink) - is DeepLink.Contacts -> addContactsAction(deeplink.data(), isQrData) - is DeepLink.Send -> sendAction(deeplink, isQrData) - is DeepLink.UserProfile -> addContactsAction(deeplink.data()?.let { listOf(it) } ?: emptyList(), isQrData) - is DeepLink.TorBridges -> addTorBridges(deeplink, isQrData) - is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink, isQrData) + is DeepLink.Contacts -> addContactsAction(deeplink.data()) + is DeepLink.Send -> sendAction(deeplink) + is DeepLink.UserProfile -> addContactsAction(deeplink.data()?.let { listOf(it) } ?: emptyList()) + is DeepLink.TorBridges -> addTorBridges(deeplink) + is DeepLink.PaperWallet -> showPaperWalletDialog(context, deeplink) } } - private fun showAddBaseNodeDialog(context: Activity, deeplink: DeepLink.AddBaseNode, isQrData: Boolean = true) { + private fun showAddBaseNodeDialog(context: Activity, deeplink: DeepLink.AddBaseNode) { val baseNode = deeplink.data() dialogManager.replace( context = context, @@ -86,13 +86,13 @@ class DeeplinkManager @Inject constructor( confirmButtonText = resourceManager.getString(R.string.common_lets_do_it), onConfirm = { dialogManager.dismiss(DialogId.DEEPLINK_ADD_BASE_NODE) - addBaseNodeAction(baseNode, isQrData) + addBaseNodeAction(baseNode) }, ).getModular(baseNode, resourceManager), ) } - private fun addUserProfile(context: Activity, deeplink: DeepLink.UserProfile, isQrData: Boolean) { + private fun addUserProfile(context: Activity, deeplink: DeepLink.UserProfile) { val contact = DeepLink.Contacts( listOf( DeepLink.Contacts.DeeplinkContact( @@ -101,10 +101,10 @@ class DeeplinkManager @Inject constructor( ) ) ) - showAddContactsDialog(context, contact, isQrData) + showAddContactsDialog(context, contact) } - private fun showAddContactsDialog(context: Activity, deeplink: DeepLink.Contacts, isQrData: Boolean = true) { + private fun showAddContactsDialog(context: Activity, deeplink: DeepLink.Contacts) { val contactDtos = deeplink.data() if (contactDtos.isEmpty()) return val names = contactDtos.joinToString(", ") { it.contactInfo.getAlias().trim() } @@ -116,7 +116,7 @@ class DeeplinkManager @Inject constructor( HeadModule(resourceManager.getString(R.string.contact_deeplink_title)), BodyModule(resourceManager.getString(R.string.contact_deeplink_message, contactDtos.size.toString()) + ". " + names), ButtonModule(resourceManager.getString(R.string.common_confirm), ButtonStyle.Normal) { - addContactsAction(contactDtos, isQrData) + addContactsAction(contactDtos) dialogManager.dismiss(DialogId.DEEPLINK_ADD_CONTACTS) }, ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close) @@ -125,13 +125,13 @@ class DeeplinkManager @Inject constructor( ) } - private fun addTorBridges(deeplink: DeepLink.TorBridges, isQrData: Boolean) { + private fun addTorBridges(deeplink: DeepLink.TorBridges) { deeplink.torConfigurations.forEach { torSharedRepository.addTorBridgeConfiguration(it) } } - private fun showPaperWalletDialog(context: Activity, deeplink: DeepLink.PaperWallet, isQrSata: Boolean = true) { + private fun showPaperWalletDialog(context: Activity, deeplink: DeepLink.PaperWallet) { dialogManager.replace( context = context, args = ModularDialogArgs( @@ -194,17 +194,17 @@ class DeeplinkManager @Inject constructor( ContactDto(FFIContactInfo(walletAddress = tariWalletAddress, alias = this.alias)) }.getOrNull() - private fun addContactsAction(contacts: List, isQrData: Boolean) { + private fun addContactsAction(contacts: List) { applicationScope.launch(Dispatchers.IO) { contactRepository.addContactList(contacts) } } - private fun sendAction(deeplink: DeepLink.Send, isQrData: Boolean) { + private fun sendAction(deeplink: DeepLink.Send) { navigator.navigate(Navigation.TxListNavigation.ToSendWithDeeplink(deeplink)) } - private fun addBaseNodeAction(baseNodeDto: BaseNodeDto, isQrData: Boolean) { + private fun addBaseNodeAction(baseNodeDto: BaseNodeDto) { baseNodesManager.addUserBaseNode(baseNodeDto) baseNodesManager.setBaseNode(baseNodeDto) walletManager.syncBaseNode() diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index b71ff0cc4..b733f8f07 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -267,6 +267,7 @@ class WalletManager @Inject constructor( corePrefRepository.onboardingAuthSetupStarted = true corePrefRepository.onboardingAuthSetupCompleted = false corePrefRepository.onboardingDisplayedAtHome = true + corePrefRepository.needToShowRecoverySuccessDialog = true tariSettingsPrefRepository.isRestoredWallet = true } diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt index 015453816..fab23ff15 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/CorePrefRepository.kt @@ -85,6 +85,7 @@ class CorePrefRepository @Inject constructor( const val ONBOARDING_AUTH_SETUP_STARTED = "tari_wallet_onboarding_auth_setup_started" const val ONBOARDING_COMPLETED = "tari_wallet_onboarding_completed" const val ONBOARDING_DISPLAYED_AT_HOME = "tari_wallet_onboarding_displayed_at_home" + const val NEED_TO_SHOW_RECOVERY_SUCCESS_DIALOG = "NEED_TO_SHOW_RECOVERY_SUCCESS_DIALOG" const val IS_DATA_CLEARED = "tari_is_data_cleared" } @@ -112,6 +113,8 @@ class CorePrefRepository @Inject constructor( var onboardingDisplayedAtHome: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.ONBOARDING_DISPLAYED_AT_HOME)) + var needToShowRecoverySuccessDialog: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.NEED_TO_SHOW_RECOVERY_SUCCESS_DIALOG), false) + var isDataCleared: Boolean by SharedPrefBooleanDelegate(sharedPrefs, this, formatKey(Key.IS_DATA_CLEARED), true) val walletAddress: TariWalletAddress diff --git a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt index 4fb0f6de6..f24a590c8 100644 --- a/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt +++ b/app/src/main/java/com/tari/android/wallet/di/ApplicationComponent.kt @@ -66,7 +66,7 @@ import com.tari.android.wallet.ui.fragment.restore.activity.WalletRestoreActivit import com.tari.android.wallet.ui.fragment.restore.chooseRestoreOption.ChooseRestoreOptionViewModel import com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword.EnterRestorationPasswordViewModel import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsViewModel -import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringViewModel +import com.tari.android.wallet.ui.fragment.restore.walletRestoring.WalletRestoringViewModel import com.tari.android.wallet.ui.fragment.send.addAmount.AddAmountViewModel import com.tari.android.wallet.ui.fragment.send.addNote.AddNoteViewModel import com.tari.android.wallet.ui.fragment.send.addNote.gif.ChooseGIFDialogFragment diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt index 4ed485294..8e1bed4ca 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/contactBook/root/ShareViewModel.kt @@ -159,7 +159,7 @@ class ShareViewModel : CommonViewModel() { private fun onReceived(data: List) { HomeActivity.instance.get()?.let { context -> - deeplinkManager.execute(context, DeepLink.Contacts(data), false) + deeplinkManager.execute(context, DeepLink.Contacts(data)) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt index cb9abcba7..1c35744df 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeActivity.kt @@ -282,7 +282,7 @@ class HomeActivity : CommonActivity() { intent.data?.toString()?.takeIf { it.isNotEmpty() } ?.let { deeplinkString -> deeplinkManager.parseDeepLink(deeplinkString) } ?.let { deeplink -> - deeplinkManager.execute(context = this, deeplink = deeplink, isQrData = false) + deeplinkManager.execute(context = this, deeplink = deeplink) } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeViewModel.kt index 6fb80252b..e34c831a0 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/HomeViewModel.kt @@ -1,6 +1,5 @@ package com.tari.android.wallet.ui.fragment.home -import com.tari.android.wallet.application.YatAdapter import com.tari.android.wallet.ui.common.CommonViewModel import com.tari.android.wallet.ui.fragment.contactBook.data.ContactsRepository import com.tari.android.wallet.ui.fragment.contactBook.root.ShareViewModel @@ -8,9 +7,6 @@ import javax.inject.Inject class HomeViewModel : CommonViewModel() { - @Inject - lateinit var yatAdapter: YatAdapter - @Inject lateinit var contactsRepository: ContactsRepository diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt index 5574cfc9b..a34791748 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/home/navigation/TariNavigator.kt @@ -57,7 +57,7 @@ import com.tari.android.wallet.ui.fragment.pinCode.EnterPinCodeFragment import com.tari.android.wallet.ui.fragment.profile.WalletInfoFragment import com.tari.android.wallet.ui.fragment.restore.enterRestorationPassword.EnterRestorationPasswordFragment import com.tari.android.wallet.ui.fragment.restore.inputSeedWords.InputSeedWordsFragment -import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFragment +import com.tari.android.wallet.ui.fragment.restore.walletRestoring.WalletRestoringFragment import com.tari.android.wallet.ui.fragment.send.addAmount.AddAmountFragment import com.tari.android.wallet.ui.fragment.send.addNote.AddNoteFragment import com.tari.android.wallet.ui.fragment.send.common.TransactionData diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt index 8e6af9419..6e7b6eecc 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/onboarding/activity/OnboardingFlowActivity.kt @@ -56,7 +56,7 @@ import com.tari.android.wallet.ui.fragment.onboarding.activity.OnboardingFlowMod import com.tari.android.wallet.ui.fragment.onboarding.createWallet.CreateWalletFragment import com.tari.android.wallet.ui.fragment.onboarding.inroduction.IntroductionFragment import com.tari.android.wallet.ui.fragment.onboarding.localAuth.LocalAuthFragment -import com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords.WalletRestoringFragment +import com.tari.android.wallet.ui.fragment.restore.walletRestoring.WalletRestoringFragment import com.tari.android.wallet.ui.fragment.settings.networkSelection.NetworkSelectionFragment import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringFragment.kt similarity index 99% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringFragment.kt index d235eee0b..810e657d3 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringFragment.kt @@ -30,7 +30,7 @@ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords +package com.tari.android.wallet.ui.fragment.restore.walletRestoring import android.os.Bundle import android.view.LayoutInflater diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringViewModel.kt similarity index 99% rename from app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt rename to app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringViewModel.kt index 651e8c7d6..8f31eccf9 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoringFromSeedWords/WalletRestoringViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/walletRestoring/WalletRestoringViewModel.kt @@ -1,4 +1,4 @@ -package com.tari.android.wallet.ui.fragment.restore.walletRestoringFromSeedWords +package com.tari.android.wallet.ui.fragment.restore.walletRestoring import androidx.lifecycle.viewModelScope import com.tari.android.wallet.R diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt index 2fc164372..9bcb8bba3 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/tx/HomeFragmentViewModel.kt @@ -99,6 +99,8 @@ class HomeFragmentViewModel : CommonViewModel() { avatarEmoji.postValue(address.coreKeyEmojis.extractEmojis().take(1).joinToString("")) checkForDataConsent() + + showRecoverySuccessIfNeeded() } fun processItemClick(item: CommonViewHolderItem) { @@ -160,6 +162,17 @@ class HomeFragmentViewModel : CommonViewModel() { } } + private fun showRecoverySuccessIfNeeded() { + if (corePrefRepository.needToShowRecoverySuccessDialog) { + showSimpleDialog( + titleRes = R.string.recovery_success_dialog_title, + descriptionRes = R.string.recovery_success_dialog_description, + closeButtonTextRes = R.string.recovery_success_dialog_close, + onClose = { corePrefRepository.needToShowRecoverySuccessDialog = false }, + ) + } + } + private fun onServiceConnected() { subscribeToEventBus() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6e530f107..16ca0a33c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -609,6 +609,9 @@ here are no suggestions for the phrase Are you sure you want to cancel? You will lose your progress, but don\'t worry, you can always start over. + You’re all set! + Your funds have been imported and are being processed 😊 + Awesome, thanks! Enter your backup password From 63944bcd8f5867564ed49803c9c1a3286ff15dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Fri, 11 Oct 2024 15:41:40 +0200 Subject: [PATCH 27/32] Update FFI version to v1.7.0-pre.0 --- app/src/main/cpp/jniCommsConfig.cpp | 1 + app/src/main/cpp/jniSeedWords.cpp | 2 +- app/src/main/cpp/jniWallet.cpp | 42 ++++++++++--------- .../network/NetworkPrefRepositoryImpl.kt | 4 +- build.gradle | 4 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/app/src/main/cpp/jniCommsConfig.cpp b/app/src/main/cpp/jniCommsConfig.cpp index ec6624f38..a56abab05 100644 --- a/app/src/main/cpp/jniCommsConfig.cpp +++ b/app/src/main/cpp/jniCommsConfig.cpp @@ -67,6 +67,7 @@ Java_com_tari_android_wallet_ffi_FFICommsConfig_jniCreate( pDatastorePath, static_cast(jDiscoveryTimeoutSec), static_cast(jSafDurationSec), + false, errorPointer ); jEnv->ReleaseStringUTFChars(jPublicAddress, pControlServiceAddress); diff --git a/app/src/main/cpp/jniSeedWords.cpp b/app/src/main/cpp/jniSeedWords.cpp index bb7ab8ed4..54201f462 100644 --- a/app/src/main/cpp/jniSeedWords.cpp +++ b/app/src/main/cpp/jniSeedWords.cpp @@ -72,7 +72,7 @@ Java_com_tari_android_wallet_ffi_FFISeedWords_jniPushWord( return ExecuteWithError(jEnv, error, [&](int *errorPointer) { auto pSeedWords = GetPointerField(jEnv, jThis); const char *pWord = jEnv->GetStringUTFChars(jWord, JNI_FALSE); - jint result = seed_words_push_word(pSeedWords, pWord, errorPointer); + jint result = seed_words_push_word(pSeedWords, pWord, nullptr, errorPointer); jEnv->ReleaseStringUTFChars(jWord, pWord); return result; }); diff --git a/app/src/main/cpp/jniWallet.cpp b/app/src/main/cpp/jniWallet.cpp index 1d34f0f48..6bbbd7755 100644 --- a/app/src/main/cpp/jniWallet.cpp +++ b/app/src/main/cpp/jniWallet.cpp @@ -101,7 +101,7 @@ jmethodID balanceUpdatedCallbackMethodId; jmethodID walletScannedHeightCallbackMethodId; jmethodID baseNodeStatusCallbackMethodId; -void txBroadcastCallback(TariCompletedTransaction *pCompletedTransaction) { +void txBroadcastCallback(void *context, TariCompletedTransaction *pCompletedTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -111,7 +111,7 @@ void txBroadcastCallback(TariCompletedTransaction *pCompletedTransaction) { g_vm->DetachCurrentThread(); } -void txMinedCallback(TariCompletedTransaction *pCompletedTransaction) { +void txMinedCallback(void *context, TariCompletedTransaction *pCompletedTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -121,7 +121,7 @@ void txMinedCallback(TariCompletedTransaction *pCompletedTransaction) { g_vm->DetachCurrentThread(); } -void txMinedUnconfirmedCallback(TariCompletedTransaction *pCompletedTransaction, uint64_t confirmationCount) { +void txMinedUnconfirmedCallback(void *context, TariCompletedTransaction *pCompletedTransaction, uint64_t confirmationCount) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -132,7 +132,7 @@ void txMinedUnconfirmedCallback(TariCompletedTransaction *pCompletedTransaction, g_vm->DetachCurrentThread(); } -void txFauxConfirmedCallback(TariCompletedTransaction *pCompletedTransaction) { +void txFauxConfirmedCallback(void *context, TariCompletedTransaction *pCompletedTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -142,7 +142,7 @@ void txFauxConfirmedCallback(TariCompletedTransaction *pCompletedTransaction) { g_vm->DetachCurrentThread(); } -void txFauxUnconfirmedCallback(TariCompletedTransaction *pCompletedTransaction, uint64_t confirmationCount) { +void txFauxUnconfirmedCallback(void *context, TariCompletedTransaction *pCompletedTransaction, uint64_t confirmationCount) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -153,7 +153,7 @@ void txFauxUnconfirmedCallback(TariCompletedTransaction *pCompletedTransaction, g_vm->DetachCurrentThread(); } -void txReceivedCallback(TariPendingInboundTransaction *pPendingInboundTransaction) { +void txReceivedCallback(void *context, TariPendingInboundTransaction *pPendingInboundTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -163,7 +163,7 @@ void txReceivedCallback(TariPendingInboundTransaction *pPendingInboundTransactio g_vm->DetachCurrentThread(); } -void txReplyReceivedCallback(TariCompletedTransaction *pCompletedTransaction) { +void txReplyReceivedCallback(void *context, TariCompletedTransaction *pCompletedTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -173,7 +173,7 @@ void txReplyReceivedCallback(TariCompletedTransaction *pCompletedTransaction) { g_vm->DetachCurrentThread(); } -void txFinalizedCallback(TariCompletedTransaction *pCompletedTransaction) { +void txFinalizedCallback(void *context, TariCompletedTransaction *pCompletedTransaction) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -183,7 +183,7 @@ void txFinalizedCallback(TariCompletedTransaction *pCompletedTransaction) { g_vm->DetachCurrentThread(); } -void txDirectSendResultCallback(unsigned long long txId, TariTransactionSendStatus *status) { +void txDirectSendResultCallback(void *context, unsigned long long txId, TariTransactionSendStatus *status) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -194,7 +194,7 @@ void txDirectSendResultCallback(unsigned long long txId, TariTransactionSendStat } void -txCancellationCallback(TariCompletedTransaction *pCompletedTransaction, uint64_t rejectionReason) { +txCancellationCallback(void *context, TariCompletedTransaction *pCompletedTransaction, uint64_t rejectionReason) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -205,7 +205,7 @@ txCancellationCallback(TariCompletedTransaction *pCompletedTransaction, uint64_t g_vm->DetachCurrentThread(); } -void txoValidationCompleteCallback(uint64_t requestId, uint64_t status) { +void txoValidationCompleteCallback(void *context, uint64_t requestId, uint64_t status) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -216,7 +216,7 @@ void txoValidationCompleteCallback(uint64_t requestId, uint64_t status) { g_vm->DetachCurrentThread(); } -void contactsLivenessDataUpdatedCallback(TariContactsLivenessData *pTariContactsLivenessData) { +void contactsLivenessDataUpdatedCallback(void *context, TariContactsLivenessData *pTariContactsLivenessData) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -226,7 +226,7 @@ void contactsLivenessDataUpdatedCallback(TariContactsLivenessData *pTariContacts g_vm->DetachCurrentThread(); } -void transactionValidationCompleteCallback(uint64_t requestId, uint64_t status) { +void transactionValidationCompleteCallback(void *context, uint64_t requestId, uint64_t status) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -237,7 +237,7 @@ void transactionValidationCompleteCallback(uint64_t requestId, uint64_t status) g_vm->DetachCurrentThread(); } -void connectivityStatusCallback(uint64_t status) { +void connectivityStatusCallback(void *context, uint64_t status) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -247,7 +247,7 @@ void connectivityStatusCallback(uint64_t status) { g_vm->DetachCurrentThread(); } -void walletScannedHeightCallback(uint64_t height) { +void walletScannedHeightCallback(void *context, uint64_t height) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -257,7 +257,7 @@ void walletScannedHeightCallback(uint64_t height) { g_vm->DetachCurrentThread(); } -void balanceUpdatedCallback(TariBalance *pBalance) { +void balanceUpdatedCallback(void *context, TariBalance *pBalance) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -267,11 +267,11 @@ void balanceUpdatedCallback(TariBalance *pBalance) { g_vm->DetachCurrentThread(); } -void storeAndForwardMessagesReceivedCallback() { +void storeAndForwardMessagesReceivedCallback(void *context) { // no-op } -void baseNodeStatusCallback(TariBaseNodeState *pBaseNodeState) { +void baseNodeStatusCallback(void *context, TariBaseNodeState *pBaseNodeState) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -281,7 +281,7 @@ void baseNodeStatusCallback(TariBaseNodeState *pBaseNodeState) { g_vm->DetachCurrentThread(); } -void recoveringProcessCompleteCallback(uint8_t first, uint64_t second, uint64_t third) { +void recoveringProcessCompleteCallback(void *context, uint8_t first, uint64_t second, uint64_t third) { auto *jniEnv = getJNIEnv(); if (jniEnv == nullptr || callbackHandler == nullptr) { return; @@ -480,12 +480,14 @@ Java_com_tari_android_wallet_ffi_FFIWallet_jniCreate( } TariWallet *pWallet = wallet_create( + nullptr, pWalletConfig, pLogPath, logVerbosity, static_cast(maxNumberOfRollingLogFiles), static_cast(rollingLogFileMaxSizeBytes), pPassphrase, + nullptr, pSeedWords, pNetwork, pDnsPeer, @@ -580,7 +582,7 @@ Java_com_tari_android_wallet_ffi_FFIWallet_jniGetWalletAddress( jobject error) { return ExecuteWithErrorAndCast(jEnv, error, [&](int *errorPointer) { auto pWallet = GetPointerField(jEnv, jThis); - return wallet_get_tari_address(pWallet, errorPointer); + return wallet_get_tari_interactive_address(pWallet, errorPointer); }); } diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt index 1f533d6a2..893cff886 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt @@ -9,9 +9,9 @@ import com.tari.android.wallet.util.DebugConfig class NetworkPrefRepositoryImpl(sharedPrefs: SharedPreferences) : NetworkPrefRepository { - override val defaultNetwork = if (DebugConfig.mockNetwork) NETWORK_ESMERALDA else NETWORK_NEXTNET + override val defaultNetwork = if (DebugConfig.mockNetwork) NETWORK_ESMERALDA else NETWORK_ESMERALDA - override var supportedNetworks: List = if (DebugConfig.mockNetwork) listOf(NETWORK_ESMERALDA) else listOf(NETWORK_NEXTNET) + override var supportedNetworks: List = if (DebugConfig.mockNetwork) listOf(NETWORK_ESMERALDA) else listOf(NETWORK_ESMERALDA) private var _currentNetwork: Network by SharedPrefGsonDelegate( prefs = sharedPrefs, diff --git a/build.gradle b/build.gradle index 2d077ac19..c8716b349 100644 --- a/build.gradle +++ b/build.gradle @@ -6,12 +6,12 @@ buildscript { ext.coroutines_version = '1.8.0' // build & version - ext.buildNumber = 309 + ext.buildNumber = 310 ext.versionNumber = "0.27.1" // JNI libs ext.libwalletHostURL = "https://github.com/tari-project/tari/releases/download/" - ext.libwalletVersion = "v1.4.1-rc.0" + ext.libwalletVersion = "v1.7.0-pre.0" ext.libwalletMinValidVersion = "v1.4.1-rc.0" ext.libwalletx64A = "libminotari_wallet_ffi.android_x86_64.a" ext.libwalletArmA = "libminotari_wallet_ffi.android_aarch64.a" From ee1dd32229b661d854f082aba129388757a367ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Mon, 14 Oct 2024 16:31:42 +0200 Subject: [PATCH 28/32] Hide the select base nodes feature (#1232) --- .../application/baseNodes/BaseNodesManager.kt | 2 +- .../application/deeplinks/DeeplinkManager.kt | 14 +++++---- .../walletManager/WalletManager.kt | 29 +++++++++++++++---- .../inputSeedWords/InputSeedWordsFragment.kt | 4 +-- .../inputSeedWords/InputSeedWordsViewModel.kt | 12 +++++--- .../allSettings/AllSettingsViewModel.kt | 4 +-- .../TorBridgesSelectionViewModel.kt | 5 +++- .../tari/android/wallet/util/DebugConfig.kt | 2 ++ 8 files changed, 51 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt index 63dcd3003..3ca0b6102 100644 --- a/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/baseNodes/BaseNodesManager.kt @@ -112,7 +112,7 @@ class BaseNodesManager @Inject constructor( baseNodeSharedRepository.ffiBaseNodes = loadBaseNodesFromFFI(wallet) } - private fun loadBaseNodesFromFFI(wallet: FFIWallet): BaseNodeList = wallet.getBaseNodePeers() + fun loadBaseNodesFromFFI(wallet: FFIWallet): BaseNodeList = wallet.getBaseNodePeers() .mapIndexed { index, publicKey -> BaseNodeDto( name = "${networkRepository.currentNetwork.network.displayName} ${index + 1}", diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index 445e66d29..ba26e6d7e 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -86,7 +86,7 @@ class DeeplinkManager @Inject constructor( confirmButtonText = resourceManager.getString(R.string.common_lets_do_it), onConfirm = { dialogManager.dismiss(DialogId.DEEPLINK_ADD_BASE_NODE) - addBaseNodeAction(baseNode) + addBaseNodeAction(context, baseNode) }, ).getModular(baseNode, resourceManager), ) @@ -204,10 +204,14 @@ class DeeplinkManager @Inject constructor( navigator.navigate(Navigation.TxListNavigation.ToSendWithDeeplink(deeplink)) } - private fun addBaseNodeAction(baseNodeDto: BaseNodeDto) { - baseNodesManager.addUserBaseNode(baseNodeDto) - baseNodesManager.setBaseNode(baseNodeDto) - walletManager.syncBaseNode() + private fun addBaseNodeAction(context: Activity, baseNodeDto: BaseNodeDto) { + if (DebugConfig.selectBaseNodeEnabled) { + baseNodesManager.addUserBaseNode(baseNodeDto) + baseNodesManager.setBaseNode(baseNodeDto) + walletManager.syncBaseNode() + } else { + dialogManager.showNotReadyYetDialog(context) + } } private fun goToBackupAction() { diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index b733f8f07..b2d57ae4f 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -88,6 +88,7 @@ import com.tari.android.wallet.tor.TorProxyStateHandler import com.tari.android.wallet.ui.common.DialogManager import com.tari.android.wallet.ui.fragment.home.HomeActivity import com.tari.android.wallet.util.Constants +import com.tari.android.wallet.util.DebugConfig import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -210,6 +211,9 @@ class WalletManager @Inject constructor( * Syncs the wallet with the base node and validates the wallet */ fun syncBaseNode() { + if (!DebugConfig.selectBaseNodeEnabled) { + Logger.e("baseNodeSync: Base node selection is disabled, but syncBaseNode() is called") + } var currentBaseNode: BaseNodeDto? = baseNodesManager.currentBaseNode ?: return applicationScope.launch(Dispatchers.IO) { @@ -510,7 +514,7 @@ class WalletManager @Inject constructor( ConnectivityStatus.OFFLINE -> { val currentBaseNode = baseNodesManager.currentBaseNode - if (currentBaseNode == null || !currentBaseNode.isCustom) { + if (DebugConfig.selectBaseNodeEnabled && (currentBaseNode == null || !currentBaseNode.isCustom)) { baseNodesManager.setNextBaseNode() syncBaseNode() } @@ -548,11 +552,24 @@ class WalletManager @Inject constructor( } startLogFileObserver() - walletInstance?.let { baseNodesManager.refreshBaseNodeList(it) } - ?: error("Wallet instance is null when trying to refresh base node list") - if (baseNodesManager.currentBaseNode == null) { - baseNodesManager.setNextBaseNode() + baseNodesManager.loadBaseNodesFromFFI(requireWalletInstance) + .let { + logger.i( + "baseNodeSync: baseNodeList from FFI: ${ + if (it.isEmpty()) "No base nodes available!!" + else "\n${it.joinToString(separator = "\n")}" + }" + ) + } + + if (DebugConfig.selectBaseNodeEnabled) { + walletInstance?.let { baseNodesManager.refreshBaseNodeList(it) } + ?: error("Wallet instance is null when trying to refresh base node list") + if (baseNodesManager.currentBaseNode == null) { + baseNodesManager.setNextBaseNode() + } } + saveWalletAddressToSharedPrefs() } } @@ -605,7 +622,7 @@ class WalletManager @Inject constructor( if (failed) { walletValidationStatusMap.clear() val currentBaseNode = baseNodesManager.currentBaseNode - if (currentBaseNode == null || !currentBaseNode.isCustom) { + if (DebugConfig.selectBaseNodeEnabled && (currentBaseNode == null || !currentBaseNode.isCustom)) { baseNodesManager.setNextBaseNode() syncBaseNode() } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt index 37853bc6d..1fdabf345 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/inputSeedWords/InputSeedWordsFragment.kt @@ -82,8 +82,8 @@ class InputSeedWordsFragment : CommonFragment Date: Mon, 14 Oct 2024 20:05:08 +0200 Subject: [PATCH 29/32] Use privateKey value for parsing Paper Wallet QR (#1233) --- app/src/main/cpp/jniSeedWords.cpp | 18 +++++ .../wallet/application/deeplinks/DeepLink.kt | 25 +++---- .../application/deeplinks/DeeplinkManager.kt | 66 ++++++++++++++++++- .../application/deeplinks/DeeplinkParser.kt | 2 +- .../tari/android/wallet/ffi/FFISeedWords.kt | 5 ++ .../ui/dialog/modular/ModularDialogArgs.kt | 1 + .../ChooseRestoreOptionViewModel.kt | 56 +++++++++++++++- app/src/main/res/values/strings.xml | 5 ++ 8 files changed, 159 insertions(+), 19 deletions(-) diff --git a/app/src/main/cpp/jniSeedWords.cpp b/app/src/main/cpp/jniSeedWords.cpp index 54201f462..46540ccae 100644 --- a/app/src/main/cpp/jniSeedWords.cpp +++ b/app/src/main/cpp/jniSeedWords.cpp @@ -48,6 +48,24 @@ Java_com_tari_android_wallet_ffi_FFISeedWords_jniCreate( SetPointerField(jEnv, jThis, reinterpret_cast(pSeedWords)); } +extern "C" +JNIEXPORT void JNICALL +Java_com_tari_android_wallet_ffi_FFISeedWords_jniFromBase58( + JNIEnv *jEnv, + jobject jThis, + jstring jCypher, + jstring jPassphrase, + jobject error) { + ExecuteWithError(jEnv, error, [&](int *errorPointer) { + const char *pCypher = jEnv->GetStringUTFChars(jCypher, JNI_FALSE); + const char *pPassphrase = jEnv->GetStringUTFChars(jPassphrase, JNI_FALSE); + TariSeedWords *pSeedWords = seed_words_create_from_cipher(pCypher, pPassphrase, errorPointer); + jEnv->ReleaseStringUTFChars(jCypher, pCypher); + jEnv->ReleaseStringUTFChars(jPassphrase, pPassphrase); + SetPointerField(jEnv, jThis, reinterpret_cast(pSeedWords)); + }); +} + extern "C" JNIEXPORT void JNICALL Java_com_tari_android_wallet_ffi_FFISeedWords_jniGetMnemonicWordListForLanguage( diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeepLink.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeepLink.kt index 2de57c09a..6a8ac1a1e 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeepLink.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeepLink.kt @@ -35,6 +35,8 @@ package com.tari.android.wallet.application.deeplinks import android.os.Parcelable import com.tari.android.wallet.data.sharedPrefs.tor.TorBridgeConfiguration import com.tari.android.wallet.ffi.Base58 +import com.tari.android.wallet.ffi.FFISeedWords +import com.tari.android.wallet.ffi.runWithDestroy import com.tari.android.wallet.model.MicroTari import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.util.parseToBigInteger @@ -173,28 +175,27 @@ sealed class DeepLink : Parcelable { @Parcelize data class TorBridges(val torConfigurations: List) : DeepLink() - // tari://esmeralda/paper_wallet?seed_words[0]=young&seed_words[1]=shrimp&seed_words[2]=day&seed_words[3]=mountain&seed_words[4]=mammal&seed_words[5]=pond&seed_words[6]=shrimp&seed_words[7]=mammal&seed_words[8]=loyal&seed_words[9]=young&seed_words[10]=whisper&seed_words[11]=glare&seed_words[12]=stuff&seed_words[13]=around&seed_words[14]=estate&seed_words[15]=fabric&seed_words[16]=faint&seed_words[17]=goddess&seed_words[18]=crew&seed_words[19]=custom&seed_words[20]=disagree&seed_words[21]=fox&seed_words[22]=fragile&seed_words[23]=impact + // tari://esmeralda/paper_wallet?private_key=1LTWgW1kzx1e1EoY9vU1FCSDweVKjnJuNA9LysUJnFuy3x @Parcelize - data class PaperWallet(val seedWords: List) : DeepLink() { + data class PaperWallet(val privateKey: String) : DeepLink() { + + fun seedWords(passphrase: String): List? = runCatching { + FFISeedWords(this.privateKey, passphrase).runWithDestroy { seedWords -> (0 until seedWords.getLength()).map { seedWords.getAt(it) } } + }.getOrNull() + constructor(params: Map) : this( - params.filterKeys { it.startsWith("seed_words[") } - .toSortedMap(compareBy { it.substringAfter("[").substringBefore("]").toInt() }) - .values - .toList() + params[KEY_PRIVATE_KEY].orEmpty() ) - override fun getParams(): Map { - val params = hashMapOf() - seedWords.forEachIndexed { index, seedWord -> - params["seed_words[$index]"] = seedWord - } - return params + override fun getParams(): Map = hashMapOf().apply { + put(KEY_PRIVATE_KEY, privateKey) } override fun getCommand(): String = COMMAND_PAPER_WALLET companion object { const val COMMAND_PAPER_WALLET = "paper_wallet" + const val KEY_PRIVATE_KEY = "private_key" } } diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt index ba26e6d7e..93bdf6184 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkManager.kt @@ -11,12 +11,14 @@ import com.tari.android.wallet.model.TariWalletAddress import com.tari.android.wallet.ui.common.DialogManager import com.tari.android.wallet.ui.common.domain.ResourceManager import com.tari.android.wallet.ui.dialog.confirm.ConfirmDialogArgs +import com.tari.android.wallet.ui.dialog.modular.InputModularDialog import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs.DialogId import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule +import com.tari.android.wallet.ui.dialog.modular.modules.input.InputModule import com.tari.android.wallet.ui.fragment.contactBook.data.ContactsRepository import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.ContactDto import com.tari.android.wallet.ui.fragment.contactBook.data.contacts.FFIContactInfo @@ -167,7 +169,7 @@ class DeeplinkManager @Inject constructor( }, ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_remember_backup_no_button), ButtonStyle.Normal) { dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP) - replaceWalletAction(deeplink) + showEnterPassphraseDialog(context, deeplink) }, ButtonModule(resourceManager.getString(R.string.common_cancel), ButtonStyle.Close), ), @@ -175,6 +177,64 @@ class DeeplinkManager @Inject constructor( ) } + private fun showEnterPassphraseDialog(context: Activity, deeplink: DeepLink.PaperWallet) { + var saveAction: () -> Boolean = { false } + + val headModule = HeadModule( + title = resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_title), + rightButtonTitle = resourceManager.getString(R.string.common_done), + rightButtonAction = { saveAction() }, + ) + + val bodyModule = BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_body)) + + val passphraseModule = InputModule( + value = "", + hint = resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_hint), + isFirst = true, + isEnd = true, + onDoneAction = { saveAction() }, + ) + + saveAction = { + val seeds = deeplink.seedWords(passphraseModule.value.trim()) + if (seeds != null) { + dialogManager.dismiss(DialogId.DEEPLINK_PAPER_WALLET_ENTER_PASSPHRASE) + replaceWalletAction(seeds) + } else { + showPaperWalletErrorDialog(context) + } + true + } + + dialogManager.replace( + InputModularDialog( + context = context, + args = ModularDialogArgs( + dialogId = DialogId.DEEPLINK_PAPER_WALLET_ENTER_PASSPHRASE, + modules = listOf( + headModule, + bodyModule, + passphraseModule, + ), + ) + ) + ) + } + + private fun showPaperWalletErrorDialog(context: Activity) { + dialogManager.replace( + context = context, + args = ModularDialogArgs( + modules = listOf( + HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_error_title)), + BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_error_body)), + ButtonModule(resourceManager.getString(R.string.common_close), ButtonStyle.Close), + ), + ) + ) + } + private fun DeepLink.AddBaseNode.data(): BaseNodeDto = BaseNodeDto.fromDeeplink(this) private fun DeepLink.Contacts.data(): List = this.contacts.mapNotNull { @@ -221,8 +281,8 @@ class DeeplinkManager @Inject constructor( } } - private fun replaceWalletAction(deeplink: DeepLink.PaperWallet) { + private fun replaceWalletAction(seedWords: List) { walletManager.deleteWallet() - navigator.navigate(Navigation.SplashScreen(deeplink.seedWords)) + navigator.navigate(Navigation.SplashScreen(seedWords)) } } \ No newline at end of file diff --git a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt index a7e710867..9d423ff49 100644 --- a/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt +++ b/app/src/main/java/com/tari/android/wallet/application/deeplinks/DeeplinkParser.kt @@ -25,7 +25,7 @@ class DeeplinkParser @Inject constructor(private val networkRepository: NetworkP } val command = uri.path.orEmpty().trimStart('/') - val parameters = if (command == DeepLink.Contacts.COMMAND_CONTACTS || command == DeepLink.PaperWallet.COMMAND_PAPER_WALLET) { // list params + val parameters = if (command == DeepLink.Contacts.COMMAND_CONTACTS) { // list params uri.query.orEmpty().split("&").associate { val (key, value) = it.split("=") key to value diff --git a/app/src/main/java/com/tari/android/wallet/ffi/FFISeedWords.kt b/app/src/main/java/com/tari/android/wallet/ffi/FFISeedWords.kt index 6002cff20..e92b1f675 100644 --- a/app/src/main/java/com/tari/android/wallet/ffi/FFISeedWords.kt +++ b/app/src/main/java/com/tari/android/wallet/ffi/FFISeedWords.kt @@ -42,6 +42,7 @@ import com.tari.android.wallet.model.seedPhrase.SeedWordsWordPushResult class FFISeedWords() : FFIBase() { private external fun jniCreate() + private external fun jniFromBase58(cypher: String, passphrase: String, libError: FFIError) private external fun jniPushWord(word: String, libError: FFIError): Int private external fun jniGetLength(libError: FFIError): Int private external fun jniGetAt(index: Int, libError: FFIError): String @@ -53,6 +54,10 @@ class FFISeedWords() : FFIBase() { jniCreate() } + constructor(base58: Base58, passphrase: String) : this() { + runWithError { jniFromBase58(base58, passphrase, it) } + } + constructor(pointer: FFIPointer) : this() { if (pointer.isNull()) error("Pointer must not be null") this.pointer = pointer diff --git a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt index 7f47ba4ad..b6b656c3c 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/dialog/modular/ModularDialogArgs.kt @@ -17,5 +17,6 @@ data class ModularDialogArgs( const val DEEPLINK_ADD_CONTACTS = 605 const val DEEPLINK_PAPER_WALLET = 606 const val DEEPLINK_PAPER_WALLET_REMEMBER_TO_BACKUP = 606 + const val DEEPLINK_PAPER_WALLET_ENTER_PASSPHRASE = 607 } } diff --git a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt index 9a999faa2..f817e670d 100644 --- a/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt +++ b/app/src/main/java/com/tari/android/wallet/ui/fragment/restore/chooseRestoreOption/ChooseRestoreOptionViewModel.kt @@ -21,10 +21,12 @@ import com.tari.android.wallet.model.WalletError import com.tari.android.wallet.model.throwIf import com.tari.android.wallet.service.service.WalletServiceLauncher import com.tari.android.wallet.ui.common.CommonViewModel +import com.tari.android.wallet.ui.dialog.modular.ModularDialogArgs import com.tari.android.wallet.ui.dialog.modular.modules.body.BodyModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonModule import com.tari.android.wallet.ui.dialog.modular.modules.button.ButtonStyle import com.tari.android.wallet.ui.dialog.modular.modules.head.HeadModule +import com.tari.android.wallet.ui.dialog.modular.modules.input.InputModule import com.tari.android.wallet.ui.fragment.home.navigation.Navigation import com.tari.android.wallet.ui.fragment.qr.QrScannerActivity import com.tari.android.wallet.ui.fragment.qr.QrScannerSource @@ -102,7 +104,7 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { fun handleDeeplink(qrDeepLink: DeepLink) { if (qrDeepLink is DeepLink.PaperWallet) { - showPaperWalletDialog(qrDeepLink.seedWords) + showPaperWalletDialog(qrDeepLink) } else { showInvalidQrDialog() } @@ -195,18 +197,66 @@ class ChooseRestoreOptionViewModel : CommonViewModel() { _uiState.update { it.copy(isStarted = false) } } - private fun showPaperWalletDialog(seedWords: List) { + private fun showPaperWalletDialog(deepLink: DeepLink.PaperWallet) { showModularDialog( HeadModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_title)), BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_body)), ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_restore_button), ButtonStyle.Normal) { hideDialog() - restoreFromPaperWallet(seedWords) + showEnterPassphraseDialog(deepLink) }, ButtonModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_do_not_restore_button), ButtonStyle.Close), ) } + private fun showEnterPassphraseDialog(deeplink: DeepLink.PaperWallet) { + var saveAction: () -> Boolean = { false } + + val headModule = HeadModule( + title = resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_title), + rightButtonTitle = resourceManager.getString(R.string.common_done), + rightButtonAction = { saveAction() }, + ) + + val bodyModule = BodyModule(resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_body)) + + val passphraseModule = InputModule( + value = "", + hint = resourceManager.getString(R.string.restore_wallet_paper_wallet_enter_passphrase_hint), + isFirst = true, + isEnd = true, + onDoneAction = { saveAction() }, + ) + + saveAction = { + val seeds = deeplink.seedWords(passphraseModule.value.trim()) + if (seeds != null) { + hideDialog() + restoreFromPaperWallet(seeds) + } else { + showPaperWalletErrorDialog() + } + true + } + + showInputModalDialog( + ModularDialogArgs( + modules = listOf( + headModule, + bodyModule, + passphraseModule, + ), + ) + ) + } + + private fun showPaperWalletErrorDialog() { + showSimpleDialog( + title = resourceManager.getString(R.string.restore_wallet_paper_wallet_error_title), + description = resourceManager.getString(R.string.restore_wallet_paper_wallet_error_body), + ) + } + private fun showBackupFileNotFoundDialog() { showSimpleDialog( title = resourceManager.getString(R.string.restore_wallet_error_title), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16ca0a33c..c07095c79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -584,6 +584,11 @@ Do you want to back up your current wallet before replacing it? If you don\’t back it up first, you will lose all the funds in your current wallet! Yes, back it up No, just replace it + Password + Enter the paper wallet password + Password + Incorrect password + The password you entered is incorrect. Please try again. Restore With Seed Phrase From e83bfe59126d050f48f996e278a45fbb474ab733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Tue, 15 Oct 2024 08:59:49 +0200 Subject: [PATCH 30/32] Update app's version to 0.28.0(311), FFI to v1.7.0-rc.0 --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index c8716b349..d105aeeb3 100644 --- a/build.gradle +++ b/build.gradle @@ -6,12 +6,12 @@ buildscript { ext.coroutines_version = '1.8.0' // build & version - ext.buildNumber = 310 - ext.versionNumber = "0.27.1" + ext.buildNumber = 311 + ext.versionNumber = "0.28.0" // JNI libs ext.libwalletHostURL = "https://github.com/tari-project/tari/releases/download/" - ext.libwalletVersion = "v1.7.0-pre.0" + ext.libwalletVersion = "v1.7.0-rc.0" ext.libwalletMinValidVersion = "v1.4.1-rc.0" ext.libwalletx64A = "libminotari_wallet_ffi.android_x86_64.a" ext.libwalletArmA = "libminotari_wallet_ffi.android_aarch64.a" From 637661f4d428f85a44d777ec7cc142df8968425e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Tue, 15 Oct 2024 15:49:19 +0200 Subject: [PATCH 31/32] Update FFI to v1.7.0-rc.1 --- app/src/main/cpp/jniWallet.cpp | 1 + .../data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt | 4 ++-- build.gradle | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/cpp/jniWallet.cpp b/app/src/main/cpp/jniWallet.cpp index 6bbbd7755..95a2c8ee2 100644 --- a/app/src/main/cpp/jniWallet.cpp +++ b/app/src/main/cpp/jniWallet.cpp @@ -491,6 +491,7 @@ Java_com_tari_android_wallet_ffi_FFIWallet_jniCreate( pSeedWords, pNetwork, pDnsPeer, + nullptr, isDnsSecureOn, txReceivedCallback, txReplyReceivedCallback, diff --git a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt index 893cff886..1f533d6a2 100644 --- a/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt +++ b/app/src/main/java/com/tari/android/wallet/data/sharedPrefs/network/NetworkPrefRepositoryImpl.kt @@ -9,9 +9,9 @@ import com.tari.android.wallet.util.DebugConfig class NetworkPrefRepositoryImpl(sharedPrefs: SharedPreferences) : NetworkPrefRepository { - override val defaultNetwork = if (DebugConfig.mockNetwork) NETWORK_ESMERALDA else NETWORK_ESMERALDA + override val defaultNetwork = if (DebugConfig.mockNetwork) NETWORK_ESMERALDA else NETWORK_NEXTNET - override var supportedNetworks: List = if (DebugConfig.mockNetwork) listOf(NETWORK_ESMERALDA) else listOf(NETWORK_ESMERALDA) + override var supportedNetworks: List = if (DebugConfig.mockNetwork) listOf(NETWORK_ESMERALDA) else listOf(NETWORK_NEXTNET) private var _currentNetwork: Network by SharedPrefGsonDelegate( prefs = sharedPrefs, diff --git a/build.gradle b/build.gradle index d105aeeb3..64f797132 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { // JNI libs ext.libwalletHostURL = "https://github.com/tari-project/tari/releases/download/" - ext.libwalletVersion = "v1.7.0-rc.0" + ext.libwalletVersion = "v1.7.0-rc.1" ext.libwalletMinValidVersion = "v1.4.1-rc.0" ext.libwalletx64A = "libminotari_wallet_ffi.android_x86_64.a" ext.libwalletArmA = "libminotari_wallet_ffi.android_aarch64.a" From 052d41d073a13654c891b6bd259545a1ba330285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Danil=C4=8Denko?= Date: Tue, 15 Oct 2024 15:49:47 +0200 Subject: [PATCH 32/32] Validate wallet after it's created --- .../walletManager/WalletManager.kt | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt index b2d57ae4f..4893d9c71 100644 --- a/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt +++ b/app/src/main/java/com/tari/android/wallet/application/walletManager/WalletManager.kt @@ -227,20 +227,7 @@ class WalletManager @Inject constructor( baseNodeKeyFFI.destroy() logger.i("baseNodeSync:addBaseNodePeer ${if (addBaseNodeResult) "success" else "failed"}") - try { - logger.i("baseNodeSync:wallet validation:start Tx and TXO validation") - walletValidationStatusMap.clear() - walletValidationStatusMap[WalletValidationType.TXO] = WalletValidationResult(wallet.startTXOValidation(), null) - walletValidationStatusMap[WalletValidationType.TX] = WalletValidationResult(wallet.startTxValidation(), null) - logger.i( - "baseNodeSync:wallet validation:started Tx and TXO validation with " + - "request keys: ${Gson().toJson(walletValidationStatusMap.map { it.value.requestKey })}" - ) - } catch (e: Throwable) { - logger.i("baseNodeSync:wallet validation:error: ${e.message}") - walletValidationStatusMap.clear() - baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Failed) - } + validateWallet() } break } catch (e: Throwable) { @@ -570,10 +557,33 @@ class WalletManager @Inject constructor( } } + validateWallet() + saveWalletAddressToSharedPrefs() } } + private fun validateWallet() { + applicationScope.launch(Dispatchers.IO) { + doOnWalletRunning { wallet -> + try { + logger.i("baseNodeSync:wallet validation:start Tx and TXO validation") + walletValidationStatusMap.clear() + walletValidationStatusMap[WalletValidationType.TXO] = WalletValidationResult(wallet.startTXOValidation(), null) + walletValidationStatusMap[WalletValidationType.TX] = WalletValidationResult(wallet.startTxValidation(), null) + logger.i( + "baseNodeSync:wallet validation:started Tx and TXO validation with " + + "request keys: ${Gson().toJson(walletValidationStatusMap.map { it.value.requestKey })}" + ) + } catch (e: Throwable) { + logger.i("baseNodeSync:wallet validation:error: ${e.message}") + walletValidationStatusMap.clear() + baseNodeStateHandler.updateSyncState(BaseNodeSyncState.Failed) + } + } + } + } + private fun postTxNotification(tx: Tx) { txReceivedNotificationDelayedAction?.dispose() inboundTxEventNotificationTxs.add(tx)