From a801bf9f29013dd61527ab69c378ab2d8bf1f95e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Mar 2024 14:35:21 +0500 Subject: [PATCH 1/5] Updated on 2026-08-14 --- .../feature/qrscanning/QrScanningFragment.kt | 28 +++++++++++++++++-- .../QrScanningCameraDeniedBottomSheet.kt | 2 +- .../viewmodel/QrScanningViewModel.kt | 7 ++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt index 41a8b85226..1c66e62a33 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt @@ -20,10 +20,10 @@ import com.google.mlkit.vision.common.InputImage import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder -import com.tangem.feature.qrscanning.presentation.QrScanningContent -import com.tangem.feature.qrscanning.viewmodel.QrScanningViewModel import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter +import com.tangem.feature.qrscanning.presentation.QrScanningContent +import com.tangem.feature.qrscanning.viewmodel.QrScanningViewModel import dagger.hilt.android.AndroidEntryPoint import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -69,6 +69,11 @@ internal class QrScanningFragment : ComposeFragment() { requestCameraPermission() } + override fun onResume() { + super.onResume() + checkPermissionGranted() + } + override fun onDestroy() { super.onDestroy() cameraPermissionLauncher.unregister() @@ -80,11 +85,14 @@ internal class QrScanningFragment : ComposeFragment() { StatusBarTransparencyDisposable() QrScanningContent( executor = { cameraExecutor }, - analyzer = { analyzer }, + analyzer = { cameraAnalyzer }, uiState = viewModel.uiState.collectAsStateWithLifecycle().value, ) } + /** + * Method for requesting permission if there isn't one. + */ private fun requestCameraPermission() { if ( ContextCompat.checkSelfPermission( @@ -96,6 +104,20 @@ internal class QrScanningFragment : ComposeFragment() { } } + /** + * Method for checking if permission was granted after user opened Settings screen. + * If permission was granted dismiss bottom sheet. + */ + private fun checkPermissionGranted() { + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA, + ) == PackageManager.PERMISSION_GRANTED + ) { + viewModel.onDismissBottomSheetState() + } + } + companion object { fun create() = QrScanningFragment() diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningCameraDeniedBottomSheet.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningCameraDeniedBottomSheet.kt index a4ca7198dc..6edcbe2ea2 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningCameraDeniedBottomSheet.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningCameraDeniedBottomSheet.kt @@ -36,7 +36,7 @@ private fun CameraDeniedBottomSheet(content: CameraDeniedBottomSheetConfig) { val intent: Intent = Intent( Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", context.packageName, null), - ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) ContextCompat.startActivity(context, intent, null) }, ) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt index 5402f469b0..9e17c2f6a7 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt @@ -10,8 +10,9 @@ import com.tangem.feature.qrscanning.SourceType import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter import com.tangem.feature.qrscanning.presentation.QrScanningState import com.tangem.feature.qrscanning.presentation.QrScanningStateController -import com.tangem.feature.qrscanning.presentation.transformers.ShowCameraDeniedBottomSheetTransformer +import com.tangem.feature.qrscanning.presentation.transformers.DismissBottomSheetTransformer import com.tangem.feature.qrscanning.presentation.transformers.InitializeQrScanningStateTransformer +import com.tangem.feature.qrscanning.presentation.transformers.ShowCameraDeniedBottomSheetTransformer import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @@ -43,4 +44,8 @@ internal class QrScanningViewModel @Inject constructor( fun onCameraDeniedState() { stateHolder.update(ShowCameraDeniedBottomSheetTransformer(clickIntents)) } + + fun onDismissBottomSheetState() { + stateHolder.update(DismissBottomSheetTransformer()) + } } \ No newline at end of file From b2172951b37b73f4fbf1aa5bea6eb35e6d388170 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Mar 2024 14:35:37 +0500 Subject: [PATCH 2/5] Updated on 2026-08-14 --- .../java/com/tangem/feature/qrscanning/QrScanningFragment.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt index 1c66e62a33..577380dc6b 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt @@ -47,6 +47,11 @@ internal class QrScanningFragment : ComposeFragment() { private val viewModel by viewModels() private var cameraExecutor: ExecutorService by Delegates.notNull() + // Camera requires its own analyzer instance due to flow of frames needed to be analyzed. + // Each new frame can cancel previous analysis e.i. image from the gallery can be skipped. + private val cameraAnalyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) { + MLKitBarcodeAnalyzer(viewModel::onQrScanned) + } private val analyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) { MLKitBarcodeAnalyzer(viewModel::onQrScanned) } From 5d47619e8f513590d659d8eed3151753a49fabb0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Mar 2024 18:14:41 +0800 Subject: [PATCH 3/5] Updated on 2026-08-14 --- .../src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt | 2 ++ .../java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index cea2fca9cc..4566c64fae 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -703,6 +703,7 @@ internal class StateBuilder( return uiState.copy( sendCardData = uiState.sendCardData.copy( balance = cryptoCurrencyStatus.getFormattedAmount(isNeedSymbol = false), + token = cryptoCurrencyStatus, ), ) } @@ -716,6 +717,7 @@ internal class StateBuilder( return uiState.copy( receiveCardData = uiState.receiveCardData.copy( balance = cryptoCurrencyStatus.getFormattedAmount(isNeedSymbol = false), + token = cryptoCurrencyStatus, ), ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index a559fb5054..51969a1ec1 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -745,6 +745,8 @@ internal class SwapViewModel @Inject constructor( dataState = dataState.copy(toCryptoCurrency = it) stateBuilder.updateReceiveCurrencyBalance(uiState, it) } + + startLoadingQuotesFromLastState(isSilent = true) } .flowOn(dispatchers.main) .launchIn(viewModelScope) From 3f6ca7f9e814252a080bca0fa57033ff4ad7774e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Mar 2024 21:25:42 +0800 Subject: [PATCH 4/5] Updated on 2026-08-14 --- .../wallet/viewmodels/WalletViewModel.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index dab3f9be92..65ca30c4ff 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -177,14 +177,8 @@ internal class WalletViewModel @Inject constructor( is WalletsUpdateActionResolver.Action.UpdateWalletName -> { stateHolder.update(transformer = RenameWalletTransformer(action.selectedWalletId, action.name)) } - is WalletsUpdateActionResolver.Action.NoAccessibleWallets -> { - stateHolder.clear() - router.popBackStack(screen = AppScreen.Welcome) - } - is WalletsUpdateActionResolver.Action.NoWallets -> { - stateHolder.clear() - router.popBackStack(screen = AppScreen.Home) - } + is WalletsUpdateActionResolver.Action.NoAccessibleWallets -> closeScreen(screen = AppScreen.Welcome) + is WalletsUpdateActionResolver.Action.NoWallets -> closeScreen(screen = AppScreen.Home) is WalletsUpdateActionResolver.Action.Unknown -> Unit } } @@ -295,6 +289,13 @@ internal class WalletViewModel @Inject constructor( ) } + private fun closeScreen(screen: AppScreen) { + if (!screenLifecycleProvider.isBackground) { + stateHolder.clear() + router.popBackStack(screen = screen) + } + } + private fun scrollToWallet(index: Int, onConsume: () -> Unit = {}) { stateHolder.update( ScrollToWalletTransformer( From cb9612ec4ec00cb5e1f602801a34a654af1709b8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Mar 2024 18:35:59 +0300 Subject: [PATCH 5/5] Updated on 2026-08-14 --- .../BiometricUserWalletsListManager.kt | 1 + .../saveWallet/redux/SaveWalletAction.kt | 18 +++++++++--------- .../saveWallet/redux/SaveWalletMiddleware.kt | 5 +++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index daf1ce408b..160d433012 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -277,6 +277,7 @@ internal class BiometricUserWalletsListManager( prevState.copy( userWallets = newUserWallets, + encryptionKeys = encryptionKeys, isLocked = newUserWallets.any(UserWallet::isLocked), ) } else { diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt index 934f7e8880..e5625ad1d2 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt @@ -11,20 +11,20 @@ internal sealed interface SaveWalletAction : Action { val backupCardsIds: Set?, ) : SaveWalletAction - object Save : SaveWalletAction { - object Success : SaveWalletAction + data object Save : SaveWalletAction { + data object Success : SaveWalletAction data class Error(val error: TangemError) : SaveWalletAction } - object AllowToUseBiometrics : SaveWalletAction + data object AllowToUseBiometrics : SaveWalletAction - object Dismiss : SaveWalletAction + data object Dismiss : SaveWalletAction - object CloseError : SaveWalletAction - object EnrollBiometrics : SaveWalletAction { - object Enroll : SaveWalletAction - object Cancel : SaveWalletAction + data object CloseError : SaveWalletAction + data object EnrollBiometrics : SaveWalletAction { + data object Enroll : SaveWalletAction + data object Cancel : SaveWalletAction } - object SaveWalletWasShown : SaveWalletAction + data object SaveWalletWasShown : SaveWalletAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index ddf1c22d74..344a0909de 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -147,6 +147,11 @@ internal class SaveWalletMiddleware { } private fun allowToUseBiometrics(state: SaveWalletState) { + if (tangemSdkManager.needEnrollBiometrics) { + store.dispatchOnMain(SaveWalletAction.EnrollBiometrics) + return + } + val scanResponse = state.backupInfo?.scanResponse ?: store.state.globalState.scanResponse ?: return