diff --git a/core/resources/src/main/res/values/strings.xml b/core/resources/src/main/res/values/strings.xml
index adad2367b..3caa5026c 100644
--- a/core/resources/src/main/res/values/strings.xml
+++ b/core/resources/src/main/res/values/strings.xml
@@ -1600,4 +1600,5 @@
Enforce B/W
Barcode Image will be fully black and white and not colored by app\'s theme
Scan any Barcode (QR, EAN, AZTEC, …) and get it\'s content or paste your text to generate new one
+ No Barcode Found
\ No newline at end of file
diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/utils/navigation/Screen.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/utils/navigation/Screen.kt
index 6ba9ab7df..67524ecaf 100644
--- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/utils/navigation/Screen.kt
+++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/utils/navigation/Screen.kt
@@ -578,7 +578,8 @@ sealed class Screen(
@Serializable
data class ScanQrCode(
- val qrCodeContent: String? = null
+ val qrCodeContent: String? = null,
+ val uriToAnalyze: Uri? = null
) : Screen(
id = 27,
title = R.string.qr_code,
diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/BottomButtonsBlock.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/BottomButtonsBlock.kt
index c5d66efaf..a6b68f9da 100644
--- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/BottomButtonsBlock.kt
+++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/BottomButtonsBlock.kt
@@ -87,7 +87,8 @@ fun BottomButtonsBlock(
showNullDataButtonAsContainer: Boolean = false,
columnarFab: (@Composable ColumnScope.() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit,
- isPrimaryButtonEnabled: Boolean = true
+ isPrimaryButtonEnabled: Boolean = true,
+ showColumnarFabInRow: Boolean = false,
) {
AnimatedContent(
targetState = targetState,
@@ -97,9 +98,7 @@ fun BottomButtonsBlock(
) { (isNull, inside) ->
if (isNull) {
val button = @Composable {
- EnhancedFloatingActionButton(
- onClick = onSecondaryButtonClick,
- onLongClick = onSecondaryButtonLongClick,
+ Row(
modifier = Modifier
.windowInsetsPadding(
WindowInsets.navigationBars.union(
@@ -109,14 +108,24 @@ fun BottomButtonsBlock(
)
)
.padding(16.dp),
- content = {
- Spacer(Modifier.width(16.dp))
- Icon(secondaryButtonIcon, null)
- Spacer(Modifier.width(16.dp))
- Text(secondaryButtonText)
- Spacer(Modifier.width(16.dp))
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ EnhancedFloatingActionButton(
+ onClick = onSecondaryButtonClick,
+ onLongClick = onSecondaryButtonLongClick,
+ content = {
+ Spacer(Modifier.width(16.dp))
+ Icon(secondaryButtonIcon, null)
+ Spacer(Modifier.width(16.dp))
+ Text(secondaryButtonText)
+ Spacer(Modifier.width(16.dp))
+ }
+ )
+ if (showColumnarFabInRow && columnarFab != null) {
+ Column { columnarFab() }
}
- )
+ }
}
if (showNullDataButtonAsContainer) {
Row(
@@ -136,7 +145,9 @@ fun BottomButtonsBlock(
modifier = Modifier.drawHorizontalStroke(true),
actions = actions,
floatingActionButton = {
- Row {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
AnimatedVisibility(visible = isSecondaryButtonVisible) {
EnhancedFloatingActionButton(
onClick = onSecondaryButtonClick,
@@ -152,43 +163,45 @@ fun BottomButtonsBlock(
)
}
}
+ AnimatedVisibility(visible = showColumnarFabInRow) {
+ columnarFab?.let {
+ Column { it() }
+ }
+ }
AnimatedVisibility(visible = isPrimaryButtonVisible) {
- Row {
- Spacer(Modifier.width(8.dp))
- EnhancedFloatingActionButton(
- onClick = if (isPrimaryButtonEnabled) onPrimaryButtonClick
- else null,
- onLongClick = if (isPrimaryButtonEnabled) onPrimaryButtonLongClick
- else null,
- containerColor = takeColorFromScheme {
- if (isPrimaryButtonEnabled) primaryContainer
- else surfaceContainerHighest
- },
- contentColor = takeColorFromScheme {
- if (isPrimaryButtonEnabled) onPrimaryContainer
- else outline
- }
- ) {
- AnimatedContent(
- targetState = primaryButtonIcon to primaryButtonText,
- transitionSpec = { fadeIn() + scaleIn() togetherWith fadeOut() + scaleOut() }
- ) { (icon, text) ->
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center
- ) {
- if (text.isNotEmpty()) {
- Spacer(Modifier.width(16.dp))
- }
- Icon(
- imageVector = icon,
- contentDescription = null
- )
- if (text.isNotEmpty()) {
- Spacer(Modifier.width(16.dp))
- Text(text)
- Spacer(Modifier.width(16.dp))
- }
+ EnhancedFloatingActionButton(
+ onClick = if (isPrimaryButtonEnabled) onPrimaryButtonClick
+ else null,
+ onLongClick = if (isPrimaryButtonEnabled) onPrimaryButtonLongClick
+ else null,
+ containerColor = takeColorFromScheme {
+ if (isPrimaryButtonEnabled) primaryContainer
+ else surfaceContainerHighest
+ },
+ contentColor = takeColorFromScheme {
+ if (isPrimaryButtonEnabled) onPrimaryContainer
+ else outline
+ }
+ ) {
+ AnimatedContent(
+ targetState = primaryButtonIcon to primaryButtonText,
+ transitionSpec = { fadeIn() + scaleIn() togetherWith fadeOut() + scaleOut() }
+ ) { (icon, text) ->
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ if (text.isNotEmpty()) {
+ Spacer(Modifier.width(16.dp))
+ }
+ Icon(
+ imageVector = icon,
+ contentDescription = null
+ )
+ if (text.isNotEmpty()) {
+ Spacer(Modifier.width(16.dp))
+ Text(text)
+ Spacer(Modifier.width(16.dp))
}
}
}
@@ -223,7 +236,10 @@ fun BottomButtonsBlock(
EnhancedFloatingActionButton(
onClick = onSecondaryButtonClick,
onLongClick = onSecondaryButtonLongClick,
- containerColor = MaterialTheme.colorScheme.tertiaryContainer
+ containerColor = takeColorFromScheme {
+ if (isPrimaryButtonVisible) tertiaryContainer
+ else primaryContainer
+ }
) {
Icon(
imageVector = secondaryButtonIcon,
diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/utils/ScreenList.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/utils/ScreenList.kt
index 2e66d45a7..549ff35ea 100644
--- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/utils/ScreenList.kt
+++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/utils/ScreenList.kt
@@ -121,6 +121,7 @@ internal fun List.screenList(
Screen.ImageStacking(uris),
Screen.ImageSplitting(uris.firstOrNull()),
Screen.ImageCutter(uris),
+ Screen.ScanQrCode(uriToAnalyze = uris.firstOrNull()),
Screen.GradientMaker(uris),
Screen.PdfTools(
Screen.PdfTools.Type.ImagesToPdf(uris)
diff --git a/feature/root/src/main/java/ru/tech/imageresizershrinker/feature/root/presentation/components/navigation/ChildProvider.kt b/feature/root/src/main/java/ru/tech/imageresizershrinker/feature/root/presentation/components/navigation/ChildProvider.kt
index 5212001e1..8a7e7b5ab 100644
--- a/feature/root/src/main/java/ru/tech/imageresizershrinker/feature/root/presentation/components/navigation/ChildProvider.kt
+++ b/feature/root/src/main/java/ru/tech/imageresizershrinker/feature/root/presentation/components/navigation/ChildProvider.kt
@@ -52,7 +52,50 @@ import ru.tech.imageresizershrinker.feature.pdf_tools.presentation.screenLogic.P
import ru.tech.imageresizershrinker.feature.pick_color.presentation.screenLogic.PickColorFromImageComponent
import ru.tech.imageresizershrinker.feature.recognize.text.presentation.screenLogic.RecognizeTextComponent
import ru.tech.imageresizershrinker.feature.resize_convert.presentation.screenLogic.ResizeAndConvertComponent
-import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.*
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ApngTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Base64Tools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ChecksumTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Cipher
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.CollageMaker
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ColorTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Compare
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Crop
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.DeleteExif
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.DocumentScanner
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Draw
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.EasterEgg
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.EditExif
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.EraseBackground
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Filter
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.FormatConversion
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.GeneratePalette
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.GifTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.GradientMaker
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ImageCutter
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ImagePreview
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ImageSplitting
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ImageStacking
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ImageStitching
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.JxlTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.LibrariesInfo
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.LimitResize
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.LoadNetImage
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Main
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.MarkupLayers
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.MeshGradients
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.NoiseGeneration
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.PdfTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.PickColorFromImage
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.RecognizeText
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ResizeAndConvert
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.ScanQrCode
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Settings
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.SingleEdit
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.SvgMaker
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Watermarking
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.WebpTools
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.WeightResize
+import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.Zip
import ru.tech.imageresizershrinker.feature.root.presentation.screenLogic.RootComponent
import ru.tech.imageresizershrinker.feature.scan_qr_code.presentation.screenLogic.ScanQrCodeComponent
import ru.tech.imageresizershrinker.feature.settings.presentation.screenLogic.SettingsComponent
@@ -372,6 +415,7 @@ internal class ChildProvider @Inject constructor(
scanQrCodeComponentFactory(
componentContext = componentContext,
initialQrCodeContent = config.qrCodeContent,
+ uriToAnalyze = config.uriToAnalyze,
onGoBack = ::navigateBack
)
)
diff --git a/feature/scan-qr-code/build.gradle.kts b/feature/scan-qr-code/build.gradle.kts
index 30a570106..03fe4e533 100644
--- a/feature/scan-qr-code/build.gradle.kts
+++ b/feature/scan-qr-code/build.gradle.kts
@@ -26,4 +26,6 @@ android.namespace = "ru.tech.imageresizershrinker.feature.scan_qr_code"
dependencies {
implementation(projects.core.filters)
+ "marketImplementation"(libs.quickie.bundled)
+ "fossImplementation"(libs.quickie.foss)
}
\ No newline at end of file
diff --git a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/data/AndroidImageBarcodeReader.kt b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/data/AndroidImageBarcodeReader.kt
new file mode 100644
index 000000000..8fb41a971
--- /dev/null
+++ b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/data/AndroidImageBarcodeReader.kt
@@ -0,0 +1,64 @@
+/*
+ * ImageToolbox is an image editor for android
+ * Copyright (c) 2025 T8RIN (Malik Mukhametzyanov)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * You should have received a copy of the Apache License
+ * along with this program. If not, see .
+ */
+
+package ru.tech.imageresizershrinker.feature.scan_qr_code.data
+
+import android.graphics.Bitmap
+import androidx.exifinterface.media.ExifInterface
+import io.github.g00fy2.quickie.extensions.readQrCode
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
+import ru.tech.imageresizershrinker.core.domain.image.ImageGetter
+import ru.tech.imageresizershrinker.core.domain.resource.ResourceManager
+import ru.tech.imageresizershrinker.core.resources.R
+import ru.tech.imageresizershrinker.feature.scan_qr_code.domain.ImageBarcodeReader
+import javax.inject.Inject
+import kotlin.coroutines.resume
+
+internal class AndroidImageBarcodeReader @Inject constructor(
+ private val imageGetter: ImageGetter,
+ resourceManager: ResourceManager,
+ dispatchersHolder: DispatchersHolder
+) : ImageBarcodeReader, DispatchersHolder by dispatchersHolder, ResourceManager by resourceManager {
+
+ override suspend fun readBarcode(
+ image: Any
+ ): Result = withContext(defaultDispatcher) {
+ val bitmap = imageGetter.getImage(
+ data = image,
+ originalSize = false
+ )
+
+ if (bitmap == null) {
+ return@withContext Result.failure(NullPointerException(getString(R.string.something_went_wrong)))
+ }
+
+ suspendCancellableCoroutine { continuation ->
+ bitmap.readQrCode(
+ barcodeFormats = IntArray(0),
+ onSuccess = {
+ continuation.resume(Result.success(it))
+ },
+ onFailure = {
+ continuation.resume(Result.failure(it))
+ }
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/di/ScanQrCodeModule.kt b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/di/ScanQrCodeModule.kt
new file mode 100644
index 000000000..a56d4add3
--- /dev/null
+++ b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/di/ScanQrCodeModule.kt
@@ -0,0 +1,40 @@
+/*
+ * ImageToolbox is an image editor for android
+ * Copyright (c) 2025 T8RIN (Malik Mukhametzyanov)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * You should have received a copy of the Apache License
+ * along with this program. If not, see .
+ */
+
+package ru.tech.imageresizershrinker.feature.scan_qr_code.di
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import ru.tech.imageresizershrinker.feature.scan_qr_code.data.AndroidImageBarcodeReader
+import ru.tech.imageresizershrinker.feature.scan_qr_code.domain.ImageBarcodeReader
+import javax.inject.Singleton
+
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface ScanQrCodeModule {
+
+ @Binds
+ @Singleton
+ fun reader(
+ impl: AndroidImageBarcodeReader
+ ): ImageBarcodeReader
+
+
+}
\ No newline at end of file
diff --git a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/domain/ImageBarcodeReader.kt b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/domain/ImageBarcodeReader.kt
new file mode 100644
index 000000000..bc9dd5e82
--- /dev/null
+++ b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/domain/ImageBarcodeReader.kt
@@ -0,0 +1,26 @@
+/*
+ * ImageToolbox is an image editor for android
+ * Copyright (c) 2025 T8RIN (Malik Mukhametzyanov)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * You should have received a copy of the Apache License
+ * along with this program. If not, see .
+ */
+
+package ru.tech.imageresizershrinker.feature.scan_qr_code.domain
+
+interface ImageBarcodeReader {
+
+ suspend fun readBarcode(
+ image: Any
+ ): Result
+
+}
\ No newline at end of file
diff --git a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/ScanQrCodeContent.kt b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/ScanQrCodeContent.kt
index 2cbe74836..d00998831 100644
--- a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/ScanQrCodeContent.kt
+++ b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/ScanQrCodeContent.kt
@@ -19,6 +19,7 @@ package ru.tech.imageresizershrinker.feature.scan_qr_code.presentation
import android.annotation.SuppressLint
import android.graphics.Bitmap
+import android.net.Uri
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
@@ -26,7 +27,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AutoFixHigh
import androidx.compose.material.icons.outlined.QrCodeScanner
+import androidx.compose.material.icons.rounded.ImageSearch
import androidx.compose.material3.Badge
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -43,6 +46,9 @@ import androidx.compose.ui.unit.dp
import dev.shreyaspatil.capturable.controller.rememberCaptureController
import kotlinx.coroutines.launch
import ru.tech.imageresizershrinker.core.resources.R
+import ru.tech.imageresizershrinker.core.ui.theme.takeColorFromScheme
+import ru.tech.imageresizershrinker.core.ui.utils.content_pickers.Picker
+import ru.tech.imageresizershrinker.core.ui.utils.content_pickers.rememberImagePicker
import ru.tech.imageresizershrinker.core.ui.utils.helper.asClip
import ru.tech.imageresizershrinker.core.ui.utils.helper.isLandscapeOrientationAsState
import ru.tech.imageresizershrinker.core.ui.utils.helper.rememberBarcodeScanner
@@ -52,7 +58,9 @@ import ru.tech.imageresizershrinker.core.ui.widget.AdaptiveLayoutScreen
import ru.tech.imageresizershrinker.core.ui.widget.buttons.BottomButtonsBlock
import ru.tech.imageresizershrinker.core.ui.widget.buttons.ShareButton
import ru.tech.imageresizershrinker.core.ui.widget.dialogs.LoadingDialog
+import ru.tech.imageresizershrinker.core.ui.widget.dialogs.OneTimeImagePickingDialog
import ru.tech.imageresizershrinker.core.ui.widget.dialogs.OneTimeSaveLocationSelectionDialog
+import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedFloatingActionButton
import ru.tech.imageresizershrinker.core.ui.widget.modifier.scaleOnTap
import ru.tech.imageresizershrinker.core.ui.widget.other.BarcodeType
import ru.tech.imageresizershrinker.core.ui.widget.other.TopAppBarEmoji
@@ -82,6 +90,17 @@ fun ScanQrCodeContent(
)
}
+ val analyzerImagePicker = rememberImagePicker { uri: Uri ->
+ component.readBarcodeFromImage(
+ imageUri = uri,
+ onFailure = {
+ essentials.showFailureToast(
+ Throwable(context.getString(R.string.no_barcode_found), it)
+ )
+ }
+ )
+ }
+
LaunchedEffect(params.content) {
component.processFilterTemplateFromQrContent(
onSuccess = { filterName, filtersCount ->
@@ -192,8 +211,11 @@ fun ScanQrCodeContent(
var showFolderSelectionDialog by rememberSaveable {
mutableStateOf(false)
}
+ var showOneTimeImagePickingDialog by rememberSaveable {
+ mutableStateOf(false)
+ }
BottomButtonsBlock(
- targetState = (params.content.isEmpty()) to !isLandscape,
+ targetState = (params.content.isEmpty() && !isLandscape) to !isLandscape,
secondaryButtonIcon = Icons.Outlined.QrCodeScanner,
secondaryButtonText = stringResource(R.string.start_scanning),
onSecondaryButtonClick = scanner::scan,
@@ -208,6 +230,25 @@ fun ScanQrCodeContent(
},
actions = {
if (!isLandscape) actions()
+ },
+ showColumnarFabInRow = true,
+ isPrimaryButtonVisible = !isLandscape || params.content.isNotEmpty(),
+ columnarFab = {
+ EnhancedFloatingActionButton(
+ onClick = analyzerImagePicker::pickImage,
+ onLongClick = {
+ showOneTimeImagePickingDialog = true
+ },
+ containerColor = takeColorFromScheme {
+ if (params.content.isEmpty()) tertiaryContainer
+ else secondaryContainer
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Rounded.ImageSearch,
+ contentDescription = null
+ )
+ }
}
)
OneTimeSaveLocationSelectionDialog(
@@ -221,6 +262,12 @@ fun ScanQrCodeContent(
},
formatForFilenameSelection = component.getFormatForFilenameSelection()
)
+ OneTimeImagePickingDialog(
+ onDismiss = { showOneTimeImagePickingDialog = false },
+ picker = Picker.Single,
+ imagePicker = analyzerImagePicker,
+ visible = showOneTimeImagePickingDialog
+ )
},
canShowScreenData = true,
isPortrait = !isLandscape
diff --git a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/screenLogic/ScanQrCodeComponent.kt b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/screenLogic/ScanQrCodeComponent.kt
index a4d560a75..d9a10f0d3 100644
--- a/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/screenLogic/ScanQrCodeComponent.kt
+++ b/feature/scan-qr-code/src/main/java/ru/tech/imageresizershrinker/feature/scan_qr_code/presentation/screenLogic/ScanQrCodeComponent.kt
@@ -48,16 +48,19 @@ import ru.tech.imageresizershrinker.core.settings.domain.model.SettingsState
import ru.tech.imageresizershrinker.core.settings.presentation.model.toUiFont
import ru.tech.imageresizershrinker.core.ui.utils.BaseComponent
import ru.tech.imageresizershrinker.core.ui.utils.state.update
+import ru.tech.imageresizershrinker.feature.scan_qr_code.domain.ImageBarcodeReader
import ru.tech.imageresizershrinker.feature.scan_qr_code.presentation.components.QrPreviewParams
class ScanQrCodeComponent @AssistedInject internal constructor(
@Assisted componentContext: ComponentContext,
@Assisted initialQrCodeContent: String?,
+ @Assisted uriToAnalyze: Uri?,
@Assisted val onGoBack: () -> Unit,
private val fileController: FileController,
private val shareProvider: ShareProvider,
private val imageCompressor: ImageCompressor,
private val favoriteFiltersInteractor: FavoriteFiltersInteractor,
+ private val imageBarcodeReader: ImageBarcodeReader,
settingsProvider: SettingsProvider,
dispatchersHolder: DispatchersHolder
) : BaseComponent(dispatchersHolder, componentContext) {
@@ -79,14 +82,16 @@ class ScanQrCodeComponent @AssistedInject internal constructor(
private var settingsState: SettingsState = SettingsState.Default
init {
- settingsProvider.getSettingsStateFlow().onEach {
- settingsState = it
+ settingsProvider.getSettingsStateFlow().onEach { state ->
+ settingsState = state
_params.update {
it.copy(
descriptionFont = settingsState.font.toUiFont()
)
}
}.launchIn(componentScope)
+
+ uriToAnalyze?.let(::readBarcodeFromImage)
}
fun saveBitmap(
@@ -194,11 +199,30 @@ class ScanQrCodeComponent @AssistedInject internal constructor(
_params.update { params }
}
+ fun readBarcodeFromImage(
+ imageUri: Uri,
+ onFailure: (Throwable) -> Unit = {}
+ ) {
+ componentScope.launch {
+ imageBarcodeReader
+ .readBarcode(imageUri)
+ .onSuccess {
+ updateParams(
+ params.copy(
+ content = it
+ )
+ )
+ }
+ .onFailure(onFailure)
+ }
+ }
+
@AssistedFactory
fun interface Factory {
operator fun invoke(
componentContext: ComponentContext,
initialQrCodeContent: String?,
+ uriToAnalyze: Uri?,
onGoBack: () -> Unit,
): ScanQrCodeComponent
}