mirror of
https://github.com/T8RIN/ImageToolbox.git
synced 2025-05-17 21:45:59 +08:00
Added ability to scan barcode directly from image without opening camera
This commit is contained in:
@ -1600,4 +1600,5 @@
|
||||
<string name="enforce_bw">Enforce B/W</string>
|
||||
<string name="enforce_bw_sub">Barcode Image will be fully black and white and not colored by app\'s theme</string>
|
||||
<string name="barcodes_sub">Scan any Barcode (QR, EAN, AZTEC, …) and get it\'s content or paste your text to generate new one</string>
|
||||
<string name="no_barcode_found">No Barcode Found</string>
|
||||
</resources>
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -121,6 +121,7 @@ internal fun List<Uri>.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)
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
@ -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 <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
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<Bitmap, ExifInterface>,
|
||||
resourceManager: ResourceManager,
|
||||
dispatchersHolder: DispatchersHolder
|
||||
) : ImageBarcodeReader, DispatchersHolder by dispatchersHolder, ResourceManager by resourceManager {
|
||||
|
||||
override suspend fun readBarcode(
|
||||
image: Any
|
||||
): Result<String> = 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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
|
||||
}
|
@ -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 <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.feature.scan_qr_code.domain
|
||||
|
||||
interface ImageBarcodeReader {
|
||||
|
||||
suspend fun readBarcode(
|
||||
image: Any
|
||||
): Result<String>
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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<Bitmap>,
|
||||
private val imageCompressor: ImageCompressor<Bitmap>,
|
||||
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user