Updated on 2026-08-14
This commit is contained in:
commit
a0132aea5a
16 changed files with 316 additions and 61 deletions
|
|
@ -393,6 +393,10 @@
|
|||
<string name="organize_tokens_sort_by_balance">By balance</string>
|
||||
<string name="organize_tokens_title">Organize tokens</string>
|
||||
<string name="organize_tokens_ungroup">Ungroup</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Select from the gallery</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Settings</string>
|
||||
<string name="qr_scanner_camera_denied_title">Camera access denied</string>
|
||||
<string name="qr_scanner_camera_denied_text">You have not given access to your camera</string>
|
||||
<string name="receive_bottom_sheet_title">%1$s %2$s address on %3$s network</string>
|
||||
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) on %3$s network</string>
|
||||
<string name="receive_bottom_sheet_warning_message_full">Send only %s to this address. Sending any other currency will result in its irreversible loss.</string>
|
||||
|
|
|
|||
9
core/ui/src/main/res/drawable/ic_settings_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_settings_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,8C13.061,8 14.078,8.421 14.828,9.172C15.578,9.922 16,10.939 16,12C16,13.061 15.578,14.078 14.828,14.828C14.078,15.579 13.061,16 12,16C10.939,16 9.922,15.579 9.172,14.828C8.421,14.078 8,13.061 8,12C8,10.939 8.421,9.922 9.172,9.172C9.922,8.421 10.939,8 12,8ZM12,10C11.469,10 10.961,10.211 10.586,10.586C10.211,10.961 10,11.47 10,12C10,12.53 10.211,13.039 10.586,13.414C10.961,13.789 11.469,14 12,14C12.53,14 13.039,13.789 13.414,13.414C13.789,13.039 14,12.53 14,12C14,11.47 13.789,10.961 13.414,10.586C13.039,10.211 12.53,10 12,10ZM10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10ZM11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
</vector>
|
||||
|
|
@ -20,6 +20,7 @@ dependencies {
|
|||
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
implementation(deps.androidx.activity.compose)
|
||||
implementation(deps.lifecycle.compose)
|
||||
|
||||
/** Camera */
|
||||
implementation(deps.camera.camera2)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.qrscanning
|
|||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -10,6 +11,8 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
|
|
@ -45,12 +48,19 @@ internal class QrScanningFragment : ComposeFragment() {
|
|||
}
|
||||
|
||||
private val cameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (!it) parentFragmentManager.popBackStack()
|
||||
if (!it) viewModel.onCameraDeniedState()
|
||||
}
|
||||
private val galleryLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
val selectedImage = it ?: Uri.EMPTY
|
||||
if (selectedImage != Uri.EMPTY) {
|
||||
val image = InputImage.fromFilePath(requireContext(), selectedImage)
|
||||
analyzer.analyze(image)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.router = innerRouter
|
||||
viewModel.setRouter(innerRouter, galleryLauncher)
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
requestCameraPermission()
|
||||
}
|
||||
|
|
@ -70,7 +80,7 @@ internal class QrScanningFragment : ComposeFragment() {
|
|||
QrScanningContent(
|
||||
executor = { cameraExecutor },
|
||||
analyzer = { analyzer },
|
||||
uiState = viewModel.uiState,
|
||||
uiState = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
)
|
||||
setFitSystemWindows(fit = false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
||||
data class CameraDeniedBottomSheetConfig(
|
||||
val onGalleryClick: () -> Unit,
|
||||
val onCancelClick: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.core.ui.components.SimpleSettingsRow
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.qrscanning.impl.R
|
||||
|
||||
@Composable
|
||||
fun CameraDeniedBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(config) { content: CameraDeniedBottomSheetConfig ->
|
||||
CameraDeniedBottomSheet(content = content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CameraDeniedBottomSheet(content: CameraDeniedBottomSheetConfig) {
|
||||
val context = LocalContext.current
|
||||
Column {
|
||||
CameraDeniedBottomSheetHeader()
|
||||
SimpleSettingsRow(
|
||||
title = stringResource(id = R.string.qr_scanner_camera_denied_settings_button),
|
||||
icon = R.drawable.ic_settings_24,
|
||||
onItemsClick = {
|
||||
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)
|
||||
},
|
||||
)
|
||||
SimpleSettingsRow(
|
||||
title = stringResource(id = R.string.qr_scanner_camera_denied_gallery_button),
|
||||
icon = R.drawable.ic_gallery_24,
|
||||
onItemsClick = content.onGalleryClick,
|
||||
)
|
||||
SimpleSettingsRow(
|
||||
title = stringResource(id = R.string.common_close),
|
||||
icon = R.drawable.ic_close,
|
||||
onItemsClick = content.onCancelClick,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CameraDeniedBottomSheetHeader() {
|
||||
Text(
|
||||
text = stringResource(id = R.string.qr_scanner_camera_denied_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing20,
|
||||
end = TangemTheme.dimens.spacing20,
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.qr_scanner_camera_denied_text),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing20,
|
||||
end = TangemTheme.dimens.spacing20,
|
||||
top = TangemTheme.dimens.spacing3,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.*
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
|
|
@ -18,14 +19,12 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.StrokeJoin
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIconContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
|
|
@ -44,15 +43,9 @@ internal fun QrScanningContent(
|
|||
analyzer: () -> MLKitBarcodeAnalyzer,
|
||||
uiState: QrScanningState,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var isFlash by remember { mutableStateOf(false) }
|
||||
val galleryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
val selectedImage = it ?: Uri.EMPTY
|
||||
if (selectedImage != Uri.EMPTY) {
|
||||
val image = InputImage.fromFilePath(context, selectedImage)
|
||||
analyzer().analyze(image)
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(onBack = uiState.onBackClick)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -101,13 +94,16 @@ internal fun QrScanningContent(
|
|||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false),
|
||||
onClick = { galleryLauncher.launch("image/*") },
|
||||
onClick = uiState.onGalleryClick,
|
||||
),
|
||||
tint = TangemColorPalette.White,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (uiState.bottomSheetConfig != null) {
|
||||
CameraDeniedBottomSheet(uiState.bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
|
|
@ -8,5 +9,6 @@ data class QrScanningState(
|
|||
val message: TextReference?,
|
||||
val onQrScanned: (String) -> Unit,
|
||||
val onBackClick: () -> Unit,
|
||||
val onGalleryClicked: () -> Unit,
|
||||
val onGalleryClick: () -> Unit,
|
||||
val bottomSheetConfig: TangemBottomSheetConfig? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.QrScanningTransformer
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class QrScanningStateController @Inject constructor() {
|
||||
|
||||
val uiState: StateFlow<QrScanningState> get() = mutableUiState
|
||||
|
||||
val value: QrScanningState get() = uiState.value
|
||||
|
||||
private val mutableUiState: MutableStateFlow<QrScanningState> = MutableStateFlow(value = getInitialState())
|
||||
|
||||
fun update(function: (QrScanningState) -> QrScanningState) {
|
||||
mutableUiState.update(function = function)
|
||||
}
|
||||
|
||||
fun update(transformer: QrScanningTransformer) {
|
||||
mutableUiState.update(function = transformer::transform)
|
||||
}
|
||||
|
||||
private fun getInitialState(): QrScanningState {
|
||||
return QrScanningState(
|
||||
message = null,
|
||||
onBackClick = {},
|
||||
onQrScanned = {},
|
||||
onGalleryClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.feature.qrscanning.presentation.transformers
|
||||
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
|
||||
internal class DismissBottomSheetTransformer : QrScanningTransformer {
|
||||
override fun transform(prevState: QrScanningState): QrScanningState {
|
||||
return prevState.copy(
|
||||
bottomSheetConfig = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
package com.tangem.feature.qrscanning.presentation
|
||||
package com.tangem.feature.qrscanning.presentation.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningClickIntents
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.impl.R
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningClickIntents
|
||||
|
||||
internal class QrScanningStateFactory(
|
||||
val clickIntents: QrScanningClickIntents,
|
||||
) {
|
||||
internal class InitializeQrScanningStateTransformer(
|
||||
private val clickIntents: QrScanningClickIntents,
|
||||
private val source: SourceType,
|
||||
private val network: String?,
|
||||
) : QrScanningTransformer {
|
||||
|
||||
fun getInitialState(source: SourceType, network: String?): QrScanningState {
|
||||
override fun transform(prevState: QrScanningState): QrScanningState {
|
||||
val message = when (source) {
|
||||
SourceType.SEND -> network?.let { resourceReference(R.string.send_qrcode_scan_info, wrappedList(it)) }
|
||||
else -> null
|
||||
|
|
@ -20,7 +23,7 @@ internal class QrScanningStateFactory(
|
|||
message = message,
|
||||
onBackClick = clickIntents::onBackClick,
|
||||
onQrScanned = clickIntents::onQrScanned,
|
||||
onGalleryClicked = clickIntents::onGalleryClicked,
|
||||
onGalleryClick = clickIntents::onGalleryClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.qrscanning.presentation.transformers
|
||||
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
|
||||
internal interface QrScanningTransformer {
|
||||
|
||||
fun transform(prevState: QrScanningState): QrScanningState
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.feature.qrscanning.presentation.transformers
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.feature.qrscanning.presentation.CameraDeniedBottomSheetConfig
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningClickIntents
|
||||
|
||||
internal class ShowCameraDeniedBottomSheetTransformer(
|
||||
private val clickIntents: QrScanningClickIntents,
|
||||
) : QrScanningTransformer {
|
||||
|
||||
override fun transform(prevState: QrScanningState): QrScanningState {
|
||||
return prevState.copy(
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShow = true,
|
||||
onDismissRequest = clickIntents::onBackClick,
|
||||
content = CameraDeniedBottomSheetConfig(
|
||||
onCancelClick = clickIntents::onBackClick,
|
||||
onGalleryClick = clickIntents::onGalleryClicked,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal open class BaseQrScanningClickIntents {
|
||||
|
||||
protected val router: QrScanningInnerRouter get() = _router
|
||||
protected val viewModelScope: CoroutineScope get() = _viewModelScope
|
||||
protected val source: SourceType get() = _source
|
||||
protected val galleryLauncher: ActivityResultLauncher<String> get() = _galleryLauncher
|
||||
|
||||
private var _router: QrScanningInnerRouter by Delegates.notNull()
|
||||
private var _viewModelScope: CoroutineScope by Delegates.notNull()
|
||||
private var _source: SourceType by Delegates.notNull()
|
||||
|
||||
private var _galleryLauncher: ActivityResultLauncher<String> by Delegates.notNull()
|
||||
|
||||
open fun initialize(
|
||||
router: QrScanningInnerRouter,
|
||||
source: SourceType,
|
||||
galleryLauncher: ActivityResultLauncher<String>,
|
||||
coroutineScope: CoroutineScope,
|
||||
) {
|
||||
_router = router
|
||||
_viewModelScope = coroutineScope
|
||||
_source = source
|
||||
_galleryLauncher = galleryLauncher
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningStateController
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.DismissBottomSheetTransformer
|
||||
import com.tangem.feature.qrscanning.usecase.EmitQrScannedEventUseCase
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
interface QrScanningClickIntents {
|
||||
|
||||
fun onBackClick()
|
||||
|
|
@ -7,4 +15,39 @@ interface QrScanningClickIntents {
|
|||
fun onQrScanned(qrCode: String)
|
||||
|
||||
fun onGalleryClicked()
|
||||
}
|
||||
|
||||
@ViewModelScoped
|
||||
internal class QrScanningClickIntentsImplementor @Inject constructor(
|
||||
private val stateHolder: QrScanningStateController,
|
||||
private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
) : BaseQrScanningClickIntents(), QrScanningClickIntents {
|
||||
|
||||
private var isScanned = false
|
||||
|
||||
override fun onBackClick() = router.popBackStack()
|
||||
|
||||
override fun onQrScanned(qrCode: String) {
|
||||
if (qrCode.isNotBlank()) {
|
||||
if (!isScanned) {
|
||||
router.popBackStack()
|
||||
isScanned = true
|
||||
}
|
||||
viewModelScope.launch(dispatcher.main) {
|
||||
emitQrScannedEventUseCase.invoke(source, qrCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGalleryClicked() {
|
||||
galleryLauncher.launch(GALLERY_IMAGE_FILTER)
|
||||
if (stateHolder.value.bottomSheetConfig != null) {
|
||||
stateHolder.update(DismissBottomSheetTransformer())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val GALLERY_IMAGE_FILTER = "image/*"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -11,50 +9,38 @@ import com.tangem.feature.qrscanning.QrScanningRouter.Companion.SOURCE_KEY
|
|||
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.QrScanningStateFactory
|
||||
import com.tangem.feature.qrscanning.usecase.EmitQrScannedEventUseCase
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningStateController
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.ShowCameraDeniedBottomSheetTransformer
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.InitializeQrScanningStateTransformer
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@HiltViewModel
|
||||
internal class QrScanningViewModel @Inject constructor(
|
||||
private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val stateHolder: QrScanningStateController,
|
||||
private val clickIntents: QrScanningClickIntentsImplementor,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), QrScanningClickIntents {
|
||||
) : ViewModel() {
|
||||
|
||||
private val source: SourceType = savedStateHandle[SOURCE_KEY] ?: error("Source is mandatory")
|
||||
private val network: String? = savedStateHandle[NETWORK_KEY]
|
||||
|
||||
private val factory = QrScanningStateFactory(
|
||||
clickIntents = this,
|
||||
)
|
||||
val uiState: StateFlow<QrScanningState> = stateHolder.uiState
|
||||
|
||||
var router: QrScanningInnerRouter by Delegates.notNull()
|
||||
|
||||
var uiState: QrScanningState by mutableStateOf(factory.getInitialState(source, network))
|
||||
private set
|
||||
|
||||
private var isScanned = false
|
||||
|
||||
override fun onBackClick() = router.popBackStack()
|
||||
|
||||
override fun onQrScanned(qrCode: String) {
|
||||
if (qrCode.isNotBlank()) {
|
||||
if (!isScanned) {
|
||||
router.popBackStack()
|
||||
isScanned = true
|
||||
}
|
||||
viewModelScope.launch(dispatcher.main) {
|
||||
emitQrScannedEventUseCase.invoke(source, qrCode)
|
||||
}
|
||||
}
|
||||
fun setRouter(router: QrScanningInnerRouter, galleryLauncher: ActivityResultLauncher<String>) {
|
||||
clickIntents.initialize(
|
||||
router = router,
|
||||
source = source,
|
||||
galleryLauncher = galleryLauncher,
|
||||
coroutineScope = viewModelScope,
|
||||
)
|
||||
stateHolder.update(InitializeQrScanningStateTransformer(clickIntents, source, network))
|
||||
}
|
||||
|
||||
override fun onGalleryClicked() {
|
||||
// [REDACTED_JIRA]
|
||||
fun onQrScanned(qrCode: String) = clickIntents.onQrScanned(qrCode)
|
||||
|
||||
fun onCameraDeniedState() {
|
||||
stateHolder.update(ShowCameraDeniedBottomSheetTransformer(clickIntents))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue