diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 052973065a..23e26fd59a 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -393,6 +393,10 @@
By balance
Organize tokens
Ungroup
+ Select from the gallery
+ Settings
+ Camera access denied
+ You have not given access to your camera
%1$s %2$s address on %3$s network
%1$s (%2$s) on %3$s network
Send only %s to this address. Sending any other currency will result in its irreversible loss.
diff --git a/core/ui/src/main/res/drawable/ic_settings_24.xml b/core/ui/src/main/res/drawable/ic_settings_24.xml
new file mode 100644
index 0000000000..480a877aa2
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_settings_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/features/qr-scanning/impl/build.gradle.kts b/features/qr-scanning/impl/build.gradle.kts
index 712e2a79ce..e1d37506b4 100644
--- a/features/qr-scanning/impl/build.gradle.kts
+++ b/features/qr-scanning/impl/build.gradle.kts
@@ -20,6 +20,7 @@ dependencies {
implementation(deps.androidx.fragment.ktx)
implementation(deps.androidx.activity.compose)
+ implementation(deps.lifecycle.compose)
/** Camera */
implementation(deps.camera.camera2)
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 5805460bc2..a3ba7da2d9 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
@@ -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)
}
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/CameraDeniedBottomSheetConfig.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/CameraDeniedBottomSheetConfig.kt
new file mode 100644
index 0000000000..3d23eb1813
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/CameraDeniedBottomSheetConfig.kt
@@ -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
\ No newline at end of file
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
new file mode 100644
index 0000000000..a4ca7198dc
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningCameraDeniedBottomSheet.kt
@@ -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,
+ ),
+ )
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt
index 8d169703a6..6dc9d3592b 100644
--- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt
@@ -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)
+ }
}
}
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt
index d9eb27b155..e9ca30f4e1 100644
--- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt
@@ -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,
)
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateController.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateController.kt
new file mode 100644
index 0000000000..efad8e7984
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateController.kt
@@ -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 get() = mutableUiState
+
+ val value: QrScanningState get() = uiState.value
+
+ private val mutableUiState: MutableStateFlow = 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 = {},
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/DismissBottomSheetTransformer.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/DismissBottomSheetTransformer.kt
new file mode 100644
index 0000000000..bd3d2fb621
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/DismissBottomSheetTransformer.kt
@@ -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,
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateFactory.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt
similarity index 57%
rename from features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateFactory.kt
rename to features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt
index 0b7ef2c4da..c4df653e5a 100644
--- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningStateFactory.kt
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt
@@ -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,
)
}
}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/QrScanningTransformer.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/QrScanningTransformer.kt
new file mode 100644
index 0000000000..16dcdfbd77
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/QrScanningTransformer.kt
@@ -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
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/ShowCameraDeniedBottomSheetTransformer.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/ShowCameraDeniedBottomSheetTransformer.kt
new file mode 100644
index 0000000000..5677a023f8
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/ShowCameraDeniedBottomSheetTransformer.kt
@@ -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,
+ ),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/BaseQrScanningClickIntents.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/BaseQrScanningClickIntents.kt
new file mode 100644
index 0000000000..3ae564a745
--- /dev/null
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/BaseQrScanningClickIntents.kt
@@ -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 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 by Delegates.notNull()
+
+ open fun initialize(
+ router: QrScanningInnerRouter,
+ source: SourceType,
+ galleryLauncher: ActivityResultLauncher,
+ coroutineScope: CoroutineScope,
+ ) {
+ _router = router
+ _viewModelScope = coroutineScope
+ _source = source
+ _galleryLauncher = galleryLauncher
+ }
+}
\ No newline at end of file
diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningClickIntents.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningClickIntents.kt
index 38219460a6..7ecbdae562 100644
--- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningClickIntents.kt
+++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningClickIntents.kt
@@ -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/*"
+ }
}
\ No newline at end of file
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 b0eef6d35c..5402f469b0 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
@@ -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 = 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) {
+ 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))
}
}
\ No newline at end of file