Updated on 2026-08-14
This commit is contained in:
parent
96aebcdba2
commit
477aa3c2c4
42 changed files with 447 additions and 166 deletions
|
|
@ -96,6 +96,8 @@ dependencies {
|
|||
implementation(projects.features.manageTokens.api)
|
||||
implementation(projects.features.manageTokens.impl)
|
||||
implementation(projects.features.send.impl)
|
||||
implementation(projects.features.qrScanning.api)
|
||||
implementation(projects.features.qrScanning.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
@ -143,6 +145,7 @@ dependencies {
|
|||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Other libraries */
|
||||
|
|
@ -155,7 +158,6 @@ dependencies {
|
|||
implementation(deps.timber)
|
||||
implementation(deps.reKotlin)
|
||||
implementation(deps.zxing.qrCore)
|
||||
implementation(deps.otaliastudiosCameraView)
|
||||
implementation(deps.coil)
|
||||
implementation(deps.appsflyer)
|
||||
implementation(deps.amplitude)
|
||||
|
|
|
|||
|
|
@ -137,10 +137,6 @@
|
|||
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.lifecycle.flowWithLifecycle
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import arrow.core.getOrElse
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
|
|
@ -134,6 +135,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var sendRouter: SendRouter
|
||||
|
||||
@Inject
|
||||
lateinit var qrScanningRouter: QrScanningRouter
|
||||
|
||||
internal val viewModel: MainViewModel by viewModels()
|
||||
|
||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
|
||||
|
|
@ -206,6 +210,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
manageTokensRouter = manageTokensRouter,
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
sendRouter = sendRouter,
|
||||
qrScanningRouter = qrScanningRouter,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRe
|
|||
import com.tangem.tap.features.details.ui.details.DetailsFragment
|
||||
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
|
||||
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
|
||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
||||
import com.tangem.tap.features.home.HomeFragment
|
||||
|
|
@ -171,7 +170,11 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
.getEntryFragment()
|
||||
}
|
||||
AppScreen.WalletConnectSessions -> WalletConnectFragment()
|
||||
AppScreen.QrScan -> QrScanFragment()
|
||||
AppScreen.QrScanning -> {
|
||||
store.state.daggerGraphState
|
||||
.get(getDependency = DaggerGraphState::qrScanningRouter)
|
||||
.getEntryFragment()
|
||||
}
|
||||
AppScreen.ReferralProgram -> ReferralFragment()
|
||||
AppScreen.Swap -> SwapFragment()
|
||||
AppScreen.Welcome -> WelcomeFragment()
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
package com.tangem.tap.common.qrCodeScan
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PreviewBinder
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.LayoutQrScanningBinding
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanQrCodeActivity : AppCompatActivity() {
|
||||
|
||||
private val binding: LayoutQrScanningBinding by viewBinding(LayoutQrScanningBinding::bind)
|
||||
|
||||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||
private var cameraExecutor: ExecutorService? = null
|
||||
|
||||
private val binder = PreviewBinder()
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
|
||||
if (!permissionIsGranted()) requestPermission()
|
||||
|
||||
setContentView(R.layout.layout_qr_scanning)
|
||||
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
cameraProviderFuture?.addListener(
|
||||
{
|
||||
val cameraProvider = cameraProviderFuture?.get()
|
||||
binder.bindPreview(
|
||||
context = this,
|
||||
binding = binding,
|
||||
lifecycleOwner = this,
|
||||
cameraProvider = requireNotNull(cameraProvider),
|
||||
cameraExecutor = requireNotNull(cameraExecutor),
|
||||
onScanned = { result ->
|
||||
setResult(SCAN_QR_REQUEST_CODE, Intent().apply { putExtra(SCAN_RESULT, result) })
|
||||
finish()
|
||||
},
|
||||
)
|
||||
},
|
||||
ContextCompat.getMainExecutor(this),
|
||||
)
|
||||
|
||||
binding.overlay.post {
|
||||
binding.overlay.setViewFinder()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode != PERMISSION_REQUEST_CODE) return
|
||||
|
||||
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun permissionIsGranted(): Boolean {
|
||||
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
return cameraPermission == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
private fun requestPermission() {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SCAN_QR_REQUEST_CODE = 1001
|
||||
const val SCAN_RESULT = "scanResult"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.feature.qrscanning.repo.QrScanningEventsRepository
|
||||
import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object QrScanningDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideListenToQrScanUseCase(repository: QrScanningEventsRepository): ListenToQrScanningUseCase {
|
||||
return ListenToQrScanningUseCase(repository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import androidx.core.os.bundleOf
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
|
|
@ -16,6 +17,8 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.walletconnect.WalletConnectActions
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -98,7 +101,14 @@ class WalletConnectMiddleware {
|
|||
if (uri != null && isWalletConnectUri(uri)) {
|
||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.QrScan))
|
||||
store.dispatchOnMain(
|
||||
NavigationAction.NavigateTo(
|
||||
screen = AppScreen.QrScanning,
|
||||
bundle = bundleOf(
|
||||
QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.SelectNetwork -> {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
|
|
@ -23,22 +24,25 @@ internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber<Wallet
|
|||
@Inject
|
||||
override lateinit var appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
private val viewModel = WalletConnectViewModel(store)
|
||||
private var screenState: MutableState<WalletConnectScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.walletConnectState))
|
||||
private val viewModel: WalletConnectViewModel by viewModels()
|
||||
|
||||
private var screenState: MutableState<WalletConnectScreenState>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Analytics.send(WalletConnect.ScreenOpened())
|
||||
lifecycle.addObserver(viewModel)
|
||||
screenState = mutableStateOf(viewModel.updateState(store.state.walletConnectState))
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
val state = screenState?.value ?: return
|
||||
WalletConnectScreen(
|
||||
modifier = modifier,
|
||||
state = screenState.value,
|
||||
state = state,
|
||||
onBackClick = {
|
||||
if (screenState.value.isLoading) {
|
||||
if (state.isLoading) {
|
||||
store.dispatch(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
store.state.walletConnectState.newSessionData?.session?.session,
|
||||
|
|
@ -66,6 +70,6 @@ internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber<Wallet
|
|||
|
||||
override fun newState(state: WalletConnectState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state)
|
||||
screenState?.value = viewModel.updateState(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,34 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import androidx.lifecycle.*
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.rekotlin.Store
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class WalletConnectViewModel @Inject constructor(
|
||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
viewModelScope.launch {
|
||||
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
|
||||
.getOrElse { emptyFlow() }
|
||||
.flowWithLifecycle(owner.lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.collect { store.dispatch(WalletConnectAction.OpenSession(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class WalletConnectViewModel(private val store: Store<AppState>) {
|
||||
fun updateState(state: WalletConnectState): WalletConnectScreenState {
|
||||
Timber.d("WC2 Sessions: ${state.wc2Sessions}")
|
||||
val sessions = state.sessions.map { wcSession -> WcSessionForScreen.fromSession(wcSession) } + state.wc2Sessions
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
|||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -19,7 +22,14 @@ object ClipboardOrScanQrDialog {
|
|||
store.dispatch(WalletConnectAction.OpenSession(wcUri))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.QrScan))
|
||||
store.dispatch(
|
||||
NavigationAction.NavigateTo(
|
||||
screen = AppScreen.QrScanning,
|
||||
bundle = bundleOf(
|
||||
QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
|
|
|
|||
|
|
@ -3,30 +3,38 @@
|
|||
package com.tangem.tap.features.send.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.method.DigitsKeyListener
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.postDelayed
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import arrow.core.getOrElse
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.tangem.Message
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase
|
||||
import com.tangem.sdk.extensions.hideSoftKeyboard
|
||||
import com.tangem.tap.common.KeyboardObserver
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
import com.tangem.tap.common.extensions.setOnImeActionListener
|
||||
import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
import com.tangem.tap.common.snackBar.MaxAmountSnackbar
|
||||
import com.tangem.tap.common.text.truncateMiddleWith
|
||||
|
|
@ -49,8 +57,11 @@ import com.tangem.wallet.databinding.FragmentSendBinding
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.DecimalFormatSymbols
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val EDIT_TEXT_INPUT_DEBOUNCE = 400L
|
||||
|
||||
|
|
@ -73,11 +84,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
|
||||
val binding: FragmentSendBinding by viewBinding(FragmentSendBinding::bind)
|
||||
|
||||
@Inject
|
||||
lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycle.addObserver(viewModel)
|
||||
sendSubscriber.initViewModel(viewModel)
|
||||
Analytics.send(Token.Send.ScreenOpened())
|
||||
listenToQrCode()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
|
@ -145,13 +160,34 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
}
|
||||
imvQrCode.setOnClickListener {
|
||||
Analytics.send(Token.Send.ButtonQRCode())
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), ScanQrCodeActivity::class.java),
|
||||
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE,
|
||||
|
||||
store.dispatchOnMain(
|
||||
NavigationAction.NavigateTo(
|
||||
screen = AppScreen.QrScanning,
|
||||
bundle = bundleOf(
|
||||
QrScanningRouter.SOURCE_KEY to SourceType.SEND,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun listenToQrCode() {
|
||||
lifecycleScope.launch {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
.flowWithLifecycle(this@SendFragment.lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.collect {
|
||||
delay(200)
|
||||
|
||||
// Delayed launch is needed in order for the UI to be drawn and to process the sent events.
|
||||
// If do not use the delay, then etAmount error field is not displayed when
|
||||
// inserting an incorrect amount by shareUri
|
||||
onCodeScanned(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTransactionExtrasLayout() = with(binding.lSendAddress) {
|
||||
// TODO: [REDACTED_TASK_KEY]
|
||||
etXlmMemo.inputtedTextAsFlow()
|
||||
|
|
@ -200,27 +236,16 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
.launchIn(mainScope)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return
|
||||
|
||||
val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: ""
|
||||
private fun onCodeScanned(scannedCode: String) {
|
||||
if (scannedCode.isEmpty()) return
|
||||
|
||||
// Delayed launch is needed in order for the UI to be drawn and to process the sent events.
|
||||
// If do not use the delay, then etAmount error field is not displayed when
|
||||
// inserting an incorrect amount by shareUri
|
||||
binding.lSendAddress.imvQrCode.postDelayed(
|
||||
{
|
||||
store.dispatch(
|
||||
PasteAddress(
|
||||
data = scannedCode,
|
||||
sourceType = Token.Send.AddressEntered.SourceType.QRCode,
|
||||
),
|
||||
)
|
||||
store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused))
|
||||
},
|
||||
200,
|
||||
store.dispatch(
|
||||
PasteAddress(
|
||||
data = scannedCode,
|
||||
sourceType = Token.Send.AddressEntered.SourceType.QRCode,
|
||||
),
|
||||
)
|
||||
store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused))
|
||||
}
|
||||
|
||||
private fun setupAmountLayout() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.proxy.redux
|
||||
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
|
|
@ -21,5 +22,6 @@ sealed interface DaggerGraphAction : Action {
|
|||
val manageTokensRouter: ManageTokensRouter,
|
||||
val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
val sendRouter: SendRouter,
|
||||
val qrScanningRouter: QrScanningRouter,
|
||||
) : DaggerGraphAction
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ object DaggerGraphReducer {
|
|||
manageTokensRouter = action.manageTokensRouter,
|
||||
cardSdkConfigRepository = action.cardSdkConfigRepository,
|
||||
sendRouter = action.sendRouter,
|
||||
qrScanningRouter = action.qrScanningRouter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.proxy.redux
|
||||
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
||||
|
|
@ -50,6 +51,7 @@ data class DaggerGraphState(
|
|||
val networksRepository: NetworksRepository? = null,
|
||||
val sendFeatureToggles: SendFeatureToggles? = null,
|
||||
val sendRouter: SendRouter? = null,
|
||||
val qrScanningRouter: QrScanningRouter? = null,
|
||||
|
||||
// FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList
|
||||
val currenciesRepository: CurrenciesRepository? = null,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,5 @@
|
|||
<dimen name="access_code_info_items_space">16dp</dimen>
|
||||
<dimen name="access_code_btn_continue_margin_bottom">38dp</dimen>
|
||||
|
||||
<dimen name="qr_border_stroke_width">4dp</dimen>
|
||||
<dimen name="qr_border_corner_radius">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ enum class AppScreen(val isDialogFragment: Boolean = false) {
|
|||
ManageTokens,
|
||||
AddCustomToken,
|
||||
WalletConnectSessions,
|
||||
QrScan,
|
||||
QrScanning,
|
||||
ReferralProgram,
|
||||
Swap,
|
||||
Welcome,
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ package com.tangem.features.managetokens.navigation
|
|||
import androidx.fragment.app.Fragment
|
||||
|
||||
interface ManageTokensRouter {
|
||||
|
||||
fun getEntryFragment(): Fragment
|
||||
}
|
||||
1
features/qr-scanning/api/.gitignore
vendored
Normal file
1
features/qr-scanning/api/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
15
features/qr-scanning/api/build.gradle.kts
Normal file
15
features/qr-scanning/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.feature.qrscanning.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
interface QrScanningRouter {
|
||||
|
||||
fun getEntryFragment(): Fragment
|
||||
|
||||
companion object {
|
||||
const val SOURCE_KEY = "source"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
enum class SourceType {
|
||||
WALLET_CONNECT,
|
||||
SEND,
|
||||
}
|
||||
1
features/qr-scanning/impl/.gitignore
vendored
Normal file
1
features/qr-scanning/impl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
47
features/qr-scanning/impl/build.gradle.kts
Normal file
47
features/qr-scanning/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.feature.qrscanning.impl"
|
||||
|
||||
viewBinding {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
implementation(deps.viewBindingDelegate)
|
||||
|
||||
/** Camera */
|
||||
implementation(deps.camera.camera2)
|
||||
implementation(deps.camera.lifecycle)
|
||||
implementation(deps.camera.view)
|
||||
|
||||
implementation(deps.listenableFuture)
|
||||
implementation(deps.mlKit.barcodeScanning)
|
||||
|
||||
/** Excluded dependencies */
|
||||
implementation("com.google.guava:guava:30.0-android") {
|
||||
// excludes version 9999.0-empty-to-avoid-conflict-with-guava
|
||||
exclude(group="com.google.guava", module = "listenablefuture")
|
||||
}
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
implementation(projects.features.qrScanning.api)
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.foundation)
|
||||
|
||||
implementation(deps.arrow.core)
|
||||
}
|
||||
21
features/qr-scanning/impl/proguard-rules.pro
vendored
Normal file
21
features/qr-scanning/impl/proguard-rules.pro
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
const val SCAN_QR_REQUEST_CODE = 1001
|
||||
const val SCAN_RESULT = "scanResult"
|
||||
const val PERMISSION_REQUEST_CODE = 16
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
internal class DefaultQrScanningRouter : QrScanningRouter {
|
||||
|
||||
override fun getEntryFragment(): Fragment = QrScanningFragment.create()
|
||||
}
|
||||
|
|
@ -1,46 +1,42 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
package com.tangem.feature.qrscanning
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.otaliastudios.cameraview.CameraView
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PreviewBinder
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.LayoutQrScanningBinding
|
||||
import com.tangem.feature.qrscanning.impl.R
|
||||
import com.tangem.feature.qrscanning.impl.databinding.LayoutQrScanningBinding
|
||||
import com.tangem.feature.qrscanning.inner.PreviewBinder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
// import com.tangem.core.navigation.NavigationAction
|
||||
// import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
// import com.tangem.tap.features.details.ui.walletconnect.dialogs.PreviewBinder
|
||||
// import com.tangem.tap.store
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) {
|
||||
@AndroidEntryPoint
|
||||
internal class QrScanningFragment : Fragment(R.layout.layout_qr_scanning) {
|
||||
|
||||
private val binding: LayoutQrScanningBinding by viewBinding(LayoutQrScanningBinding::bind)
|
||||
|
||||
private val binder = PreviewBinder()
|
||||
|
||||
private val viewModel by viewModels<QrScanningViewModel>()
|
||||
|
||||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||
private var cameraExecutor: ExecutorService? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setFitSystemWindows(fit = true)
|
||||
activity?.onBackPressedDispatcher?.addCallback(
|
||||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
|
@ -61,10 +57,10 @@ internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) {
|
|||
cameraProvider = requireNotNull(cameraProvider),
|
||||
cameraExecutor = requireNotNull(cameraExecutor),
|
||||
onScanned = { result ->
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
parentFragmentManager.popBackStack()
|
||||
setFitSystemWindows(fit = false)
|
||||
if (result.isNotBlank()) {
|
||||
store.dispatch(WalletConnectAction.OpenSession(result))
|
||||
viewModel.onQrScanned(result)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -83,11 +79,10 @@ internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) {
|
|||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
if (requestCode != CameraView.PERMISSION_REQUEST_CODE) return
|
||||
if (requestCode != PERMISSION_REQUEST_CODE) return
|
||||
|
||||
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
store.dispatch(WalletConnectAction.NotifyCameraPermissionIsRequired)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +92,7 @@ internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) {
|
|||
}
|
||||
|
||||
private fun requestPermission() {
|
||||
requestPermissions(arrayOf(Manifest.permission.CAMERA), CameraView.PERMISSION_REQUEST_CODE)
|
||||
requestPermissions(arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE)
|
||||
}
|
||||
|
||||
private fun setFitSystemWindows(fit: Boolean) {
|
||||
|
|
@ -105,4 +100,9 @@ internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) {
|
|||
WindowCompat.setDecorFitsSystemWindows(it, fit)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create() = QrScanningFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import com.tangem.feature.qrscanning.repo.DefaultQrScanningEventsRepository
|
||||
import com.tangem.feature.qrscanning.repo.QrScanningEventsRepository
|
||||
import com.tangem.feature.qrscanning.usecase.EmitQrScannedEventUseCase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object QrScanningModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideQrScannedEventsRepository(): QrScanningEventsRepository {
|
||||
return DefaultQrScanningEventsRepository()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideEmitQrScannedEventUseCase(repository: QrScanningEventsRepository): EmitQrScannedEventUseCase {
|
||||
return EmitQrScannedEventUseCase(repository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object QrScanningRouterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideQrScanRouter(): QrScanningRouter {
|
||||
return DefaultQrScanningRouter()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter.Companion.SOURCE_KEY
|
||||
import com.tangem.feature.qrscanning.usecase.EmitQrScannedEventUseCase
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class QrScanningViewModel @Inject constructor(
|
||||
private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
private val source: SourceType = savedStateHandle[SOURCE_KEY] ?: error("Source is mandatory")
|
||||
|
||||
fun onQrScanned(qrCode: String) {
|
||||
viewModelScope.launch {
|
||||
emitQrScannedEventUseCase.invoke(source, qrCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.common.qrCodeScan
|
||||
package com.tangem.feature.qrscanning.inner
|
||||
|
||||
import androidx.camera.core.ExperimentalGetImage
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
||||
package com.tangem.feature.qrscanning.inner
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Size
|
||||
|
|
@ -9,8 +9,7 @@ import androidx.camera.core.ImageAnalysis
|
|||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.tangem.tap.common.qrCodeScan.MLKitBarcodeAnalyzer
|
||||
import com.tangem.wallet.databinding.LayoutQrScanningBinding
|
||||
import com.tangem.feature.qrscanning.impl.databinding.LayoutQrScanningBinding
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
internal class PreviewBinder {
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.tap.common.qrCodeScan
|
||||
package com.tangem.feature.qrscanning.inner
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.feature.qrscanning.impl.R
|
||||
|
||||
class ViewFinderOverlay(context: Context, attrs: AttributeSet) : View(context, attrs) {
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.feature.qrscanning.repo
|
||||
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class DefaultQrScanningEventsRepository : QrScanningEventsRepository {
|
||||
|
||||
private data class QrScanningEvent(val type: SourceType, val qrCode: String)
|
||||
|
||||
private val scannedEvents = MutableSharedFlow<QrScanningEvent>()
|
||||
|
||||
override suspend fun emitResult(type: SourceType, qrCode: String) {
|
||||
scannedEvents.emit(QrScanningEvent(type, qrCode))
|
||||
}
|
||||
|
||||
override fun subscribeToScanningResults(type: SourceType) = scannedEvents
|
||||
.filter { it.type == type }
|
||||
.map { it.qrCode }
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.feature.qrscanning.repo
|
||||
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface QrScanningEventsRepository {
|
||||
|
||||
suspend fun emitResult(type: SourceType, qrCode: String)
|
||||
|
||||
fun subscribeToScanningResults(type: SourceType): Flow<String>
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.feature.qrscanning.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.repo.QrScanningEventsRepository
|
||||
|
||||
internal class EmitQrScannedEventUseCase(
|
||||
private val repository: QrScanningEventsRepository,
|
||||
) {
|
||||
suspend operator fun invoke(type: SourceType, qrCode: String): Either<Exception, Unit> {
|
||||
return try {
|
||||
repository.emitResult(type, qrCode)
|
||||
Unit.right()
|
||||
} catch (e: Exception) {
|
||||
e.left()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.feature.qrscanning.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.repo.QrScanningEventsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.lang.Exception
|
||||
|
||||
class ListenToQrScanningUseCase(
|
||||
val repository: QrScanningEventsRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(type: SourceType): Either<Exception, Flow<String>> {
|
||||
return try {
|
||||
repository.subscribeToScanningResults(type).right()
|
||||
} catch (e: Exception) {
|
||||
e.left()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.tangem.tap.common.qrCodeScan.ViewFinderOverlay
|
||||
<com.tangem.feature.qrscanning.inner.ViewFinderOverlay
|
||||
android:id="@+id/overlay"
|
||||
android:layerType="software"
|
||||
android:layout_width="match_parent"
|
||||
4
features/qr-scanning/impl/src/main/res/values/colors.xml
Normal file
4
features/qr-scanning/impl/src/main/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
7
features/qr-scanning/impl/src/main/res/values/dimens.xml
Normal file
7
features/qr-scanning/impl/src/main/res/values/dimens.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<dimen name="qr_border_stroke_width">4dp</dimen>
|
||||
<dimen name="qr_border_corner_radius">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
@ -59,7 +59,6 @@ kotsonGsonExt = "2.5.0"
|
|||
lottie = "3.4.0"
|
||||
moshi = "1.12.0"
|
||||
okhttp = "4.9.3"
|
||||
otaliastudiosCameraView = "2.6.3"
|
||||
rekotlin = "1.0.4"
|
||||
retrofit = "2.8.1"
|
||||
retrofitMoshiConverter = "2.6.0"
|
||||
|
|
@ -215,7 +214,6 @@ moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
|
|||
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
|
||||
okHttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
okHttp-prettyLogging = { module = "com.github.ihsanbal:LoggingInterceptor", version.ref = "okHttp-prettyLogging" }
|
||||
otaliastudiosCameraView = { module = "com.otaliastudios:cameraview", version.ref = "otaliastudiosCameraView" }
|
||||
shopify-buy = { module = "com.shopify.mobilebuysdk:buy3", version.ref = "shopifyBuySdk" }
|
||||
spongecastle-core = { module = "com.madgag.spongycastle:core", version.ref = "spongycastleCryptoCore" }
|
||||
reKotlin = { module = "org.rekotlin:rekotlin", version.ref = "rekotlin" }
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ dependencyResolutionManagement {
|
|||
google()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
jcenter() // unable to replace with mavenCentral() due to rekotlin and com.otaliastudios:cameraview
|
||||
jcenter() // unable to replace with mavenCentral() due to rekotlin
|
||||
maven("https://nexus.tangem-tech.com/repository/maven-releases/")
|
||||
maven {
|
||||
// setting any repository from tangem project allows maven search all packages in the project
|
||||
|
|
@ -101,6 +101,9 @@ include(":features:send:impl")
|
|||
|
||||
include(":features:manage-tokens:api")
|
||||
include(":features:manage-tokens:impl")
|
||||
|
||||
include(":features:qr-scanning:api")
|
||||
include(":features:qr-scanning:impl")
|
||||
// endregion Feature modules
|
||||
|
||||
// region Domain modules
|
||||
|
|
@ -140,4 +143,4 @@ include(":data:settings")
|
|||
include(":data:txhistory")
|
||||
include(":data:wallets")
|
||||
include(":data:analytics")
|
||||
// endregion Data modules
|
||||
// endregion Data modules
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue