diff --git a/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/ImageToolboxApplication.kt b/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/ImageToolboxApplication.kt index b328313244..733ff6ab35 100644 --- a/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/ImageToolboxApplication.kt +++ b/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/ImageToolboxApplication.kt @@ -21,21 +21,14 @@ import android.app.Application import com.arkivanov.decompose.DecomposeExperimentFlags import com.arkivanov.decompose.ExperimentalDecomposeApi import dagger.hilt.android.HiltAndroidApp -import org.bouncycastle.jce.provider.BouncyCastleProvider -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType -import java.security.Security +import ru.tech.imageresizershrinker.app.presentation.components.utils.registerSecurityProviders @HiltAndroidApp class ImageToolboxApplication : Application() { init { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) - Security.addProvider(BouncyCastleProvider()) - ChecksumType.registerSecurityMessageDigests( - Security.getAlgorithms("MessageDigest") - .filterNotNull() - ) + registerSecurityProviders() } @OptIn(ExperimentalDecomposeApi::class) diff --git a/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/utils/Security.kt b/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/utils/Security.kt new file mode 100644 index 0000000000..f94f0311d3 --- /dev/null +++ b/app/src/main/java/ru/tech/imageresizershrinker/app/presentation/components/utils/Security.kt @@ -0,0 +1,55 @@ +/* + * 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.app.presentation.components.utils + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.bouncycastle.asn1.ASN1ObjectIdentifier +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.operator.DefaultAlgorithmNameFinder +import ru.tech.imageresizershrinker.core.domain.model.CipherType +import ru.tech.imageresizershrinker.core.domain.model.HashingType +import java.security.Security +import kotlin.text.all + +internal fun registerSecurityProviders() { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) + Security.addProvider(BouncyCastleProvider()) + + HashingType.registerSecurityMessageDigests( + Security.getAlgorithms("MessageDigest").filterNotNull() + ) + + CoroutineScope(Dispatchers.Default).launch { + val finder = DefaultAlgorithmNameFinder() + + CipherType.registerSecurityCiphers( + Security.getAlgorithms("Cipher").filterNotNull().map { cipher -> + val oid = cipher.removePrefix("OID.") + if (oid.all { it.isDigit() || it.isWhitespace() || it == '.' }) { + finder.getAlgorithmName( + ASN1ObjectIdentifier(oid) + ) + } else { + oid + } + } + ) + } +} \ No newline at end of file diff --git a/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/saving/AndroidFilenameCreator.kt b/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/saving/AndroidFilenameCreator.kt index 28cdc29277..8814c81063 100644 --- a/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/saving/AndroidFilenameCreator.kt +++ b/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/saving/AndroidFilenameCreator.kt @@ -69,7 +69,7 @@ internal class AndroidFilenameCreator @Inject constructor( ): String { val extension = saveTarget.extension - val checksumType = settingsState.checksumTypeForFilename + val checksumType = settingsState.hashingTypeForFilename if (checksumType != null && saveTarget.data.isNotEmpty()) { val name = checksumType.computeFromByteArray(saveTarget.data) diff --git a/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/utils/ChecksumUtils.kt b/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/utils/ChecksumUtils.kt index c66216e8ed..d2f0ec10fe 100644 --- a/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/utils/ChecksumUtils.kt +++ b/core/data/src/main/java/ru/tech/imageresizershrinker/core/data/utils/ChecksumUtils.kt @@ -17,22 +17,22 @@ package ru.tech.imageresizershrinker.core.data.utils -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.saving.io.Readable import java.io.InputStream import java.security.MessageDigest private const val STREAM_BUFFER_LENGTH = 1024 -fun ChecksumType.computeFromReadable( +fun HashingType.computeFromReadable( readable: Readable ): String = computeFromByteArray(readable.readBytes()) -internal fun ChecksumType.computeFromByteArray( +internal fun HashingType.computeFromByteArray( byteArray: ByteArray ): String = computeFromInputStream(byteArray.inputStream()) -internal fun ChecksumType.computeFromInputStream( +internal fun HashingType.computeFromInputStream( inputStream: InputStream ): String = inputStream.buffered().use { val byteArray = updateDigest(it).digest() @@ -92,11 +92,11 @@ internal fun encodeHex( /** * Reads through an InputStream and updates the digest for the data * - * @param ChecksumType The ChecksumType to use (e.g. MD5) + * @param HashingType The ChecksumType to use (e.g. MD5) * @param data Data to digest * @return the digest */ -private fun ChecksumType.updateDigest( +private fun HashingType.updateDigest( data: InputStream ): MessageDigest { val digest = toDigest() @@ -123,4 +123,4 @@ private val DIGITS_UPPER = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') -private fun ChecksumType.toDigest(): MessageDigest = MessageDigest.getInstance(digest) \ No newline at end of file +private fun HashingType.toDigest(): MessageDigest = MessageDigest.getInstance(digest) \ No newline at end of file diff --git a/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/CipherType.kt b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/CipherType.kt new file mode 100644 index 0000000000..fbc6373715 --- /dev/null +++ b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/CipherType.kt @@ -0,0 +1,69 @@ +/* + * 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.core.domain.model + +@ConsistentCopyVisibility +/** + * [CipherType] multiplatform domain wrapper for java Cipher, in order to add custom digests, you need to call [registerSecurityCiphers] when process created + **/ +data class CipherType private constructor( + val cipher: String, + val name: String = cipher +) { + companion object { + val AES_NO_PADDING = CipherType("AES/GCM/NoPadding") + + private var securityCiphers: List? = null + + fun registerSecurityCiphers(ciphers: List) { + if (!securityCiphers.isNullOrEmpty()) { + throw IllegalArgumentException("SecurityCiphers already registered") + } + securityCiphers = ciphers.distinctBy { it.replace("OID.", "").uppercase() } + } + + val entries: List by lazy { + val available = securityCiphers?.mapNotNull { cipher -> + if (cipher.isEmpty()) null + else { + val strippedCipher = cipher.replace("OID.", "") + SecureAlgorithmsMapping.findMatch(strippedCipher)?.let { mapping -> + CipherType( + cipher = cipher, + name = mapping.algorithm + ) + } ?: CipherType(cipher = cipher) + } + }?.sortedBy { it.cipher } ?: emptyList() + + listOf( + AES_NO_PADDING + ).let { + it + available + }.distinctBy { it.name.replace("-", "") } + } + + fun fromString( + cipher: String? + ): CipherType? = cipher?.let { + entries.find { + it.cipher == cipher + } + } + } +} \ No newline at end of file diff --git a/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumType.kt b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/HashingType.kt similarity index 73% rename from core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumType.kt rename to core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/HashingType.kt index bd19e1da36..a477ca13a1 100644 --- a/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumType.kt +++ b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/HashingType.kt @@ -19,19 +19,19 @@ package ru.tech.imageresizershrinker.core.domain.model @ConsistentCopyVisibility /** - * [ChecksumType] multiplatform domain wrapper for java MessageDigest, in order to add custom digests, you need to call [registerSecurityMessageDigests] when process created + * [HashingType] multiplatform domain wrapper for java MessageDigest, in order to add custom digests, you need to call [registerSecurityMessageDigests] when process created **/ -data class ChecksumType private constructor( +data class HashingType private constructor( val digest: String, val name: String = digest ) { companion object { - val MD5 = ChecksumType("MD5") - val SHA_1 = ChecksumType("SHA-1") - val SHA_224 = ChecksumType("SHA-224") - val SHA_256 = ChecksumType("SHA-256") - val SHA_384 = ChecksumType("SHA-384") - val SHA_512 = ChecksumType("SHA-512") + val MD5 = HashingType("MD5") + val SHA_1 = HashingType("SHA-1") + val SHA_224 = HashingType("SHA-224") + val SHA_256 = HashingType("SHA-256") + val SHA_384 = HashingType("SHA-384") + val SHA_512 = HashingType("SHA-512") private var securityMessageDigests: List? = null @@ -39,20 +39,20 @@ data class ChecksumType private constructor( if (!securityMessageDigests.isNullOrEmpty()) { throw IllegalArgumentException("SecurityMessageDigests already registered") } - securityMessageDigests = digests.distinctBy { it.replace("OID.", "") } + securityMessageDigests = digests.distinctBy { it.replace("OID.", "").uppercase() } } - val entries: List by lazy { + val entries: List by lazy { val available = securityMessageDigests?.mapNotNull { messageDigest -> if (messageDigest.isEmpty()) null else { val digest = messageDigest.replace("OID.", "") - ChecksumTypeMapping.findMatch(digest)?.let { mapping -> - ChecksumType( + SecureAlgorithmsMapping.findMatch(digest)?.let { mapping -> + HashingType( digest = messageDigest, name = mapping.algorithm ) - } ?: ChecksumType(digest = messageDigest) + } ?: HashingType(digest = messageDigest) } }?.sortedBy { it.digest } ?: emptyList() @@ -70,7 +70,7 @@ data class ChecksumType private constructor( fun fromString( digest: String? - ): ChecksumType? = digest?.let { + ): HashingType? = digest?.let { entries.find { it.digest == digest } diff --git a/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumTypeMapping.kt b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/SecureAlgorithmsMapping.kt similarity index 88% rename from core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumTypeMapping.kt rename to core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/SecureAlgorithmsMapping.kt index 168efb2ee6..e68d499c7d 100644 --- a/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/ChecksumTypeMapping.kt +++ b/core/domain/src/main/kotlin/ru/tech/imageresizershrinker/core/domain/model/SecureAlgorithmsMapping.kt @@ -24,7 +24,7 @@ package ru.tech.imageresizershrinker.core.domain.model * is determined by existing usage and may be in lowercase/uppercase in * order to match existing output. */ -enum class ChecksumTypeMapping { +enum class SecureAlgorithmsMapping { // X.500 Attributes 2.5.4.* CommonName("2.5.4.3"), Surname("2.5.4.4"), @@ -424,7 +424,44 @@ enum class ChecksumTypeMapping { JAVASOFT_JCEKeyProtector("1.3.6.1.4.1.42.2.19.1"), MICROSOFT_ExportApproved("1.3.6.1.4.1.311.10.3.3"), - Blowfish("1.3.6.1.4.1.3029.1.1.2"); + Blowfish("1.3.6.1.4.1.3029.1.1.2"), + + asn1_module("1.2.410.200046.1.1.1", "ASN1-module"), + aria256_ecb("1.2.410.200046.1.1.11", "ARIA256/ECB"), + aria256_cbc("1.2.410.200046.1.1.12", "ARIA256/CBC"), + aria256_cfb("1.2.410.200046.1.1.13", "ARIA256/CFB"), + aria256_ofb("1.2.410.200046.1.1.14", "ARIA256/OFB"), + aria128_cbc("1.2.410.200046.1.1.2", "ARIA128/CBC"), + aria128_cfb("1.2.410.200046.1.1.3", "ARIA128/CFB"), + aria128_ofb("1.2.410.200046.1.1.4", "ARIA128/OFB"), + aria192_ecb("1.2.410.200046.1.1.6", "ARIA192/ECB"), + aria192_cbc("1.2.410.200046.1.1.7", "ARIA192/CBC"), + aria192_cfb("1.2.410.200046.1.1.8", "ARIA192/CFB"), + aria192_ofb("1.2.410.200046.1.1.9", "ARIA192/OFB"), + GOST_R28147_89("1.2.643.2.2.13.0", "GOST-R28147-89"), + CryptoPro("1.2.643.2.2.13.1", "CryptoPro"), + GOST_28147_89("1.2.643.2.2.21", "GOST-28147-89"), + dstu7624_ecb_128("1.2.804.2.1.1.1.1.1.3.1.1", "DSTU7624/ECB-128"), + dstu7624_ecb_256("1.2.804.2.1.1.1.1.1.3.1.2", "DSTU7624/ECB-256"), + dstu7624_ecb_512("1.2.804.2.1.1.1.1.1.3.1.3", "DSTU7624/ECB-512"), + dstu7624_ctr_128("1.2.804.2.1.1.1.1.1.3.2.1", "DSTU7624/CTR-128"), + dstu7624_ctr_256("1.2.804.2.1.1.1.1.1.3.2.2", "DSTU7624/CTR-256"), + dstu7624_ctr_512("1.2.804.2.1.1.1.1.1.3.2.3", "DSTU7624/CTR-512"), + dstu7624_cfb_128("1.2.804.2.1.1.1.1.1.3.3.1", "DSTU7624/CFB-128"), + dstu7624_cfb_256("1.2.804.2.1.1.1.1.1.3.3.2", "DSTU7624/CFB-256"), + dstu7624_cfb_512("1.2.804.2.1.1.1.1.1.3.3.3", "DSTU7624/CFB-512"), + dstu7624_cbc_128("1.2.804.2.1.1.1.1.1.3.5.1", "DSTU7624/CBC-128"), + dstu7624_cbc_256("1.2.804.2.1.1.1.1.1.3.5.2", "DSTU7624/CBC-256"), + dstu7624_cbc_512("1.2.804.2.1.1.1.1.1.3.5.3", "DSTU7624/CBC-512"), + dstu7624_ofb_128("1.2.804.2.1.1.1.1.1.3.6.1", "DSTU7624/OFB-128"), + dstu7624_ofb_256("1.2.804.2.1.1.1.1.1.3.6.2", "DSTU7624/OFB-256"), + dstu7624_ofb_512("1.2.804.2.1.1.1.1.1.3.6.3", "DSTU7624/OFB-512"), + dstu7624_ccm_128("1.2.804.2.1.1.1.1.1.3.8.1", "DSTU7624/CCM-128"), + dstu7624_ccm_256("1.2.804.2.1.1.1.1.1.3.8.2", "DSTU7624/CCM-256"), + dstu7624_ccm_512("1.2.804.2.1.1.1.1.1.3.8.3", "DSTU7624/CCM-512"), + CMS3DESwrap("1.2.840.113549.1.9.16.3.6", "CMS3DESwrap"), + des_CBC("1.3.14.3.2.7", "DES/CBC"), + Joint_RSA("2.5.8.1.1", "Joint-RSA"); val algorithm: String val oid: String @@ -449,7 +486,7 @@ enum class ChecksumTypeMapping { companion object { fun findMatch( oidOrName: String - ): ChecksumTypeMapping? = ChecksumTypeMapping.entries.find { + ): SecureAlgorithmsMapping? = SecureAlgorithmsMapping.entries.find { it.oid == oidOrName || it.algorithm == oidOrName || it.aliases.contains(oidOrName) } } diff --git a/core/resources/src/main/res/values/strings.xml b/core/resources/src/main/res/values/strings.xml index 3c37b5af6e..b73276e825 100644 --- a/core/resources/src/main/res/values/strings.xml +++ b/core/resources/src/main/res/values/strings.xml @@ -265,7 +265,7 @@ Pick background color and draw on top of it Background color Cipher - Encrypt and Decrypt any file (not only the image) based on AES crypto algorithm + Encrypt and Decrypt any file (not only the image) based on various available crypto algorithms Pick file Encrypt Decrypt @@ -279,7 +279,7 @@ Implementation Compatibility Password-based encryption of files. Proceeded files can be stored in the selected directory or shared. Decrypted files can also be opened directly. - AES-256, GCM mode, no padding, 12 bytes random IVs. Keys are used as SHA-3 hashes (256 bits). + AES-256, GCM mode, no padding, 12 bytes random IVs. (By default, but you can select needed algorithm) Keys are used as SHA-3 hashes (256 bits). File size The maximum file size is restricted by the Android OS and available memory, which is device dependent. \nPlease note: memory is not storage. diff --git a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/SettingsInteractor.kt b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/SettingsInteractor.kt index 10b525a91d..1c777fae1d 100644 --- a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/SettingsInteractor.kt +++ b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/SettingsInteractor.kt @@ -19,8 +19,8 @@ package ru.tech.imageresizershrinker.core.settings.domain import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.ColorModel +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.PerformanceClass import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility import ru.tech.imageresizershrinker.core.settings.domain.model.ColorHarmonizer @@ -213,7 +213,7 @@ interface SettingsInteractor : SimpleSettingsInteractor { suspend fun setFastSettingsSide(side: FastSettingsSide) - suspend fun setChecksumTypeForFilename(type: ChecksumType?) + suspend fun setChecksumTypeForFilename(type: HashingType?) suspend fun setCustomFonts(fonts: List) diff --git a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/model/SettingsState.kt b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/model/SettingsState.kt index 54dec18e1d..98f5e05ae9 100644 --- a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/model/SettingsState.kt +++ b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/domain/model/SettingsState.kt @@ -21,9 +21,9 @@ import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.Preset import ru.tech.imageresizershrinker.core.domain.image.model.Preset.Percentage import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.ColorModel import ru.tech.imageresizershrinker.core.domain.model.DomainAspectRatio +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility data class SettingsState( @@ -113,7 +113,7 @@ data class SettingsState( val isCenterAlignDialogButtons: Boolean, val fastSettingsSide: FastSettingsSide, val settingGroupsInitialVisibility: Map, - val checksumTypeForFilename: ChecksumType?, + val hashingTypeForFilename: HashingType?, val customFonts: List ) { @@ -206,7 +206,7 @@ data class SettingsState( isCenterAlignDialogButtons = false, fastSettingsSide = FastSettingsSide.CenterStart, settingGroupsInitialVisibility = emptyMap(), - checksumTypeForFilename = null, + hashingTypeForFilename = null, customFonts = emptyList() ) } diff --git a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/presentation/model/UiSettingsState.kt b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/presentation/model/UiSettingsState.kt index 2dd7384c94..87c0cc1983 100644 --- a/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/presentation/model/UiSettingsState.kt +++ b/core/settings/src/main/java/ru/tech/imageresizershrinker/core/settings/presentation/model/UiSettingsState.kt @@ -42,8 +42,8 @@ import kotlinx.coroutines.launch import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.Preset import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.DomainAspectRatio +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility import ru.tech.imageresizershrinker.core.resources.R import ru.tech.imageresizershrinker.core.settings.domain.model.ColorHarmonizer @@ -142,7 +142,7 @@ data class UiSettingsState( val isCenterAlignDialogButtons: Boolean, val fastSettingsSide: FastSettingsSide, val settingGroupsInitialVisibility: Map, - val checksumTypeForFilename: ChecksumType?, + val hashingTypeForFilename: HashingType?, val customFonts: List ) @@ -371,7 +371,7 @@ fun SettingsState.toUiState( isCenterAlignDialogButtons = isCenterAlignDialogButtons, fastSettingsSide = fastSettingsSide, settingGroupsInitialVisibility = settingGroupsInitialVisibility, - checksumTypeForFilename = checksumTypeForFilename, + hashingTypeForFilename = hashingTypeForFilename, customFonts = customFonts ) } diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/selection/DataSelector.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/selection/DataSelector.kt index 20f4ab4244..f753266938 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/selection/DataSelector.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/selection/DataSelector.kt @@ -75,7 +75,8 @@ fun DataSelector( badgeContent: (@Composable RowScope.() -> Unit)? = null, shape: Shape = RoundedCornerShape(20.dp), color: Color = MaterialTheme.colorScheme.surface, - selectedItemColor: Color = MaterialTheme.colorScheme.tertiary + selectedItemColor: Color = MaterialTheme.colorScheme.tertiary, + initialExpanded: Boolean = false ) { Column( modifier = modifier.container( @@ -83,7 +84,7 @@ fun DataSelector( color = color ) ) { - var expanded by rememberSaveable { mutableStateOf(false) } + var expanded by rememberSaveable(initialExpanded) { mutableStateOf(initialExpanded) } Row { val rotation by animateFloatAsState(if (expanded) 180f else 0f) Row( diff --git a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/data/AndroidChecksumManager.kt b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/data/AndroidChecksumManager.kt index f974ee51b3..ce76500e86 100644 --- a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/data/AndroidChecksumManager.kt +++ b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/data/AndroidChecksumManager.kt @@ -26,7 +26,7 @@ import ru.tech.imageresizershrinker.core.data.saving.io.ByteArrayReadable import ru.tech.imageresizershrinker.core.data.saving.io.UriReadable import ru.tech.imageresizershrinker.core.data.utils.computeFromReadable import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.saving.io.Readable import ru.tech.imageresizershrinker.feature.checksum_tools.domain.ChecksumManager import ru.tech.imageresizershrinker.feature.checksum_tools.domain.ChecksumSource @@ -39,7 +39,7 @@ internal class AndroidChecksumManager @Inject constructor( ) : ChecksumManager, DispatchersHolder by dispatchersHolder { override suspend fun calculateChecksum( - type: ChecksumType, + type: HashingType, source: ChecksumSource ): String = withContext(defaultDispatcher) { runCatching { @@ -49,7 +49,7 @@ internal class AndroidChecksumManager @Inject constructor( override suspend fun compareChecksum( checksum: String, - type: ChecksumType, + type: HashingType, source: ChecksumSource ): Boolean = coroutineScope { calculateChecksum(type, source) == checksum diff --git a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/domain/ChecksumManager.kt b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/domain/ChecksumManager.kt index 0ccf2128b9..2cee80c4dc 100644 --- a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/domain/ChecksumManager.kt +++ b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/domain/ChecksumManager.kt @@ -17,18 +17,18 @@ package ru.tech.imageresizershrinker.feature.checksum_tools.domain -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType interface ChecksumManager { suspend fun calculateChecksum( - type: ChecksumType, + type: HashingType, source: ChecksumSource ): String suspend fun compareChecksum( checksum: String, - type: ChecksumType, + type: HashingType, source: ChecksumSource ): Boolean diff --git a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/ChecksumToolsContent.kt b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/ChecksumToolsContent.kt index 44d9e65d64..b0d0058799 100644 --- a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/ChecksumToolsContent.kt +++ b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/ChecksumToolsContent.kt @@ -81,7 +81,7 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.resources.R import ru.tech.imageresizershrinker.core.ui.theme.Green import ru.tech.imageresizershrinker.core.ui.theme.Red @@ -135,7 +135,7 @@ fun ChecksumToolsContent( Badge( content = { Text( - text = ChecksumType.entries.size.toString() + text = HashingType.entries.size.toString() ) }, containerColor = MaterialTheme.colorScheme.tertiary, @@ -265,11 +265,11 @@ fun ChecksumToolsContent( .padding(top = 20.dp) .padding(horizontal = 20.dp) .padding(insets), - value = component.checksumType, + value = component.hashingType, color = Color.Unspecified, selectedItemColor = MaterialTheme.colorScheme.secondary, onValueChange = component::updateChecksumType, - entries = ChecksumType.entries, + entries = HashingType.entries, title = stringResource(R.string.algorithms), titleIcon = Icons.Rounded.Tag, itemContentText = { diff --git a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/screenLogic/ChecksumToolsComponent.kt b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/screenLogic/ChecksumToolsComponent.kt index 98adde0a96..e19e70c81a 100644 --- a/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/screenLogic/ChecksumToolsComponent.kt +++ b/feature/checksum-tools/src/main/java/ru/tech/imageresizershrinker/feature/checksum_tools/presentation/screenLogic/ChecksumToolsComponent.kt @@ -26,7 +26,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.ui.utils.BaseComponent import ru.tech.imageresizershrinker.core.ui.utils.state.update import ru.tech.imageresizershrinker.feature.checksum_tools.domain.ChecksumManager @@ -41,9 +41,9 @@ class ChecksumToolsComponent @AssistedInject constructor( dispatchersHolder: DispatchersHolder ) : BaseComponent(dispatchersHolder, componentContext) { - private val _checksumType: MutableState = - mutableStateOf(ChecksumType.entries.first()) - val checksumType: ChecksumType by _checksumType + private val _hashingType: MutableState = + mutableStateOf(HashingType.entries.first()) + val hashingType: HashingType by _hashingType private val _calculateFromUriPage: MutableState = mutableStateOf(ChecksumPage.CalculateFromUri.Empty) @@ -76,7 +76,7 @@ class ChecksumToolsComponent @AssistedInject constructor( it.copy( uri = uri, checksum = checksumManager.calculateChecksum( - type = checksumType, + type = hashingType, source = ChecksumSource.Uri(uri.toString()) ) ) @@ -97,7 +97,7 @@ class ChecksumToolsComponent @AssistedInject constructor( text = text, checksum = if (text.isNotEmpty()) { checksumManager.calculateChecksum( - type = checksumType, + type = hashingType, source = ChecksumSource.Text(text) ) } else "" @@ -123,12 +123,12 @@ class ChecksumToolsComponent @AssistedInject constructor( uri = uri, targetChecksum = targetChecksum, checksum = checksumManager.calculateChecksum( - type = checksumType, + type = hashingType, source = ChecksumSource.Uri(uri.toString()) ), isCorrect = checksumManager.compareChecksum( checksum = targetChecksum, - type = checksumType, + type = hashingType, source = ChecksumSource.Uri(uri.toString()) ) ) @@ -136,8 +136,8 @@ class ChecksumToolsComponent @AssistedInject constructor( } } - fun updateChecksumType(type: ChecksumType) { - _checksumType.update { type } + fun updateChecksumType(type: HashingType) { + _hashingType.update { type } calculateFromUriPage.uri?.let(::setUri) calculateFromTextPage.text.let(::setText) compareWithUriPage.uri?.let(::setDataForComparison) diff --git a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/data/AndroidCryptographyManager.kt b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/data/AndroidCryptographyManager.kt index af2f9c38d6..37a9771a8b 100644 --- a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/data/AndroidCryptographyManager.kt +++ b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/data/AndroidCryptographyManager.kt @@ -19,6 +19,8 @@ package ru.tech.imageresizershrinker.feature.cipher.data +import ru.tech.imageresizershrinker.core.domain.model.CipherType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.feature.cipher.domain.CryptographyManager import java.security.MessageDigest import java.security.SecureRandom @@ -29,11 +31,13 @@ import javax.inject.Inject internal class AndroidCryptographyManager @Inject constructor() : CryptographyManager { - private val HASHING_ALGORITHM = "SHA-256" - private val ENCRYPTION_STANDARD = "AES/GCM/NoPadding" + private val HASHING_ALGORITHM = HashingType.SHA_256.digest private val CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - private fun createKey(password: String): SecretKeySpec { + private fun createKey( + password: String, + type: CipherType + ): SecretKeySpec { val pwBytes = password.toByteArray(charset("UTF-8")) // Create secret Key factory based on the specified algorithm @@ -42,7 +46,7 @@ internal class AndroidCryptographyManager @Inject constructor() : CryptographyMa // digest the pwBytes to be a new key md.update(pwBytes, 0, pwBytes.size) val key = md.digest() - return SecretKeySpec(key, ENCRYPTION_STANDARD) + return SecretKeySpec(key, type.cipher) } override fun generateRandomString(length: Int): String { @@ -56,30 +60,55 @@ internal class AndroidCryptographyManager @Inject constructor() : CryptographyMa override suspend fun decrypt( data: ByteArray, - key: String + key: String, + type: CipherType ): ByteArray { - val keySpec = createKey(key) - val cipher = Cipher.getInstance(ENCRYPTION_STANDARD) - cipher.init( - Cipher.DECRYPT_MODE, - keySpec, - IvParameterSpec(keySpec.encoded, 0, cipher.blockSize) + val keySpec = createKey( + password = key, + type = type ) + val cipher = type.toCipher() + try { + cipher.init( + Cipher.DECRYPT_MODE, + keySpec, + IvParameterSpec(keySpec.encoded, 0, cipher.blockSize) + ) + } catch (_: Exception) { + cipher.init( + Cipher.DECRYPT_MODE, + keySpec + ) + } return cipher.doFinal(data) } override suspend fun encrypt( data: ByteArray, - key: String + key: String, + type: CipherType ): ByteArray { - val keySpec = createKey(key) - val cipher = Cipher.getInstance(ENCRYPTION_STANDARD) - cipher.init( - Cipher.ENCRYPT_MODE, - keySpec, - IvParameterSpec(keySpec.encoded, 0, cipher.blockSize) + val keySpec = createKey( + password = key, + type = type ) + + val cipher = type.toCipher() + try { + cipher.init( + Cipher.ENCRYPT_MODE, + keySpec, + IvParameterSpec(keySpec.encoded, 0, cipher.blockSize) + ) + } catch (_: Exception) { + cipher.init( + Cipher.ENCRYPT_MODE, + keySpec + ) + } return cipher.doFinal(data) } + private fun CipherType.toCipher(): Cipher = Cipher.getInstance(cipher) + } \ No newline at end of file diff --git a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/domain/CryptographyManager.kt b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/domain/CryptographyManager.kt index d5bd48ac40..909fc39132 100644 --- a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/domain/CryptographyManager.kt +++ b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/domain/CryptographyManager.kt @@ -17,6 +17,8 @@ package ru.tech.imageresizershrinker.feature.cipher.domain +import ru.tech.imageresizershrinker.core.domain.model.CipherType + interface CryptographyManager { fun generateRandomString( @@ -25,12 +27,14 @@ interface CryptographyManager { suspend fun decrypt( data: ByteArray, - key: String + key: String, + type: CipherType ): ByteArray suspend fun encrypt( data: ByteArray, - key: String + key: String, + type: CipherType ): ByteArray } \ No newline at end of file diff --git a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/CipherContent.kt b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/CipherContent.kt index d1e46b4d76..65b03729b7 100644 --- a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/CipherContent.kt +++ b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/CipherContent.kt @@ -46,9 +46,11 @@ import androidx.compose.material.icons.rounded.CheckCircle import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material.icons.rounded.FileDownload import androidx.compose.material.icons.rounded.FileOpen +import androidx.compose.material.icons.rounded.Key import androidx.compose.material.icons.rounded.Share import androidx.compose.material.icons.rounded.Shuffle import androidx.compose.material.icons.twotone.FileOpen +import androidx.compose.material3.Badge import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor @@ -62,6 +64,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -70,6 +73,7 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import ru.tech.imageresizershrinker.core.domain.model.CipherType import ru.tech.imageresizershrinker.core.domain.utils.readableByteCount import ru.tech.imageresizershrinker.core.domain.utils.toInt import ru.tech.imageresizershrinker.core.resources.R @@ -87,6 +91,7 @@ import ru.tech.imageresizershrinker.core.ui.utils.provider.rememberLocalEssentia 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.ToggleGroupButton +import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.DataSelector import ru.tech.imageresizershrinker.core.ui.widget.dialogs.ExitWithoutSavingDialog import ru.tech.imageresizershrinker.core.ui.widget.dialogs.LoadingDialog import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedButton @@ -94,6 +99,7 @@ import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedIconButton import ru.tech.imageresizershrinker.core.ui.widget.enhanced.hapticsClickable import ru.tech.imageresizershrinker.core.ui.widget.image.AutoFilePicker import ru.tech.imageresizershrinker.core.ui.widget.modifier.container +import ru.tech.imageresizershrinker.core.ui.widget.modifier.scaleOnTap import ru.tech.imageresizershrinker.core.ui.widget.other.TopAppBarEmoji import ru.tech.imageresizershrinker.core.ui.widget.preferences.PreferenceItem import ru.tech.imageresizershrinker.core.ui.widget.text.AutoSizeText @@ -153,21 +159,40 @@ fun CipherContent( AdaptiveLayoutScreen( title = { - AnimatedContent( - targetState = component.uri to component.isEncrypt, - transitionSpec = { fadeIn() togetherWith fadeOut() }, + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier.marquee() - ) { (uri, isEncrypt) -> - Text( - if (uri == null) { - stringResource(R.string.cipher) - } else { - listOf( - stringResource(R.string.encryption), - stringResource(R.string.decryption) - )[if (isEncrypt) 0 else 1] + ) { + AnimatedContent( + targetState = component.uri to component.isEncrypt, + transitionSpec = { fadeIn() togetherWith fadeOut() } + ) { (uri, isEncrypt) -> + Text( + if (uri == null) { + stringResource(R.string.cipher) + } else { + listOf( + stringResource(R.string.encryption), + stringResource(R.string.decryption) + )[if (isEncrypt) 0 else 1] + }, + textAlign = TextAlign.Center + ) + } + Badge( + content = { + Text( + text = CipherType.entries.size.toString() + ) }, - textAlign = TextAlign.Center + containerColor = MaterialTheme.colorScheme.tertiary, + contentColor = MaterialTheme.colorScheme.onTertiary, + modifier = Modifier + .padding(horizontal = 2.dp) + .padding(bottom = 12.dp) + .scaleOnTap { + showConfetti() + } ) } }, @@ -258,260 +283,271 @@ fun CipherContent( } }, controls = { - AnimatedContent(targetState = component.uri != null) { hasUri -> - if (hasUri) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - if (isPortrait) Spacer(Modifier.height(20.dp)) - Row( - modifier = Modifier - .container(CircleShape) - .padding(horizontal = 8.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - val items = listOf( - stringResource(R.string.encryption), - stringResource(R.string.decryption) + Column(horizontalAlignment = Alignment.CenterHorizontally) { + if (isPortrait) Spacer(Modifier.height(20.dp)) + Row( + modifier = Modifier + .container(CircleShape) + .padding(horizontal = 8.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + val items = listOf( + stringResource(R.string.encryption), + stringResource(R.string.decryption) + ) + ToggleGroupButton( + enabled = true, + itemCount = items.size, + selectedIndex = (!component.isEncrypt).toInt(), + onIndexChange = { + component.setIsEncrypt(it == 0) + }, + itemContent = { + Text( + text = items[it], + fontSize = 12.sp ) - ToggleGroupButton( - enabled = true, - itemCount = items.size, - selectedIndex = (!component.isEncrypt).toInt(), - onIndexChange = { - component.setIsEncrypt(it == 0) - }, - itemContent = { - Text( - text = items[it], - fontSize = 12.sp - ) - }, - isScrollable = false, - modifier = Modifier.weight(1f) + }, + isScrollable = false, + modifier = Modifier.weight(1f) + ) + EnhancedIconButton( + containerColor = MaterialTheme.colorScheme.secondaryContainer, + onClick = { + showTip = true + } + ) { + Icon( + imageVector = Icons.AutoMirrored.Rounded.HelpOutline, + contentDescription = "Info" + ) + } + } + component.uri?.let { uri -> + PreferenceItem( + modifier = Modifier.padding(top = 16.dp), + title = context.getFilename(uri) + ?: stringResource(R.string.something_went_wrong), + onClick = null, + titleFontStyle = LocalTextStyle.current.copy( + lineHeight = 16.sp, + fontSize = 15.sp + ), + subtitle = component.uri?.let { + stringResource( + id = R.string.size, + readableByteCount( + it.fileSize(context) ?: 0L + ) ) + }, + startIcon = Icons.AutoMirrored.Rounded.InsertDriveFile + ) + + RoundedTextField( + modifier = Modifier + .padding(top = 16.dp) + .container(shape = RoundedCornerShape(24.dp)) + .padding(8.dp), + value = key, + startIcon = { EnhancedIconButton( - containerColor = MaterialTheme.colorScheme.secondaryContainer, onClick = { - showTip = true - } + key = component.generateRandomPassword() + component.resetCalculatedData() + }, + modifier = Modifier.padding(start = 4.dp) ) { Icon( - imageVector = Icons.AutoMirrored.Rounded.HelpOutline, - contentDescription = "Info" + imageVector = Icons.Rounded.Shuffle, + contentDescription = stringResource(R.string.shuffle), + tint = MaterialTheme.colorScheme.onSecondaryContainer ) } + }, + endIcon = { + EnhancedIconButton( + onClick = { + key = "" + component.resetCalculatedData() + }, + modifier = Modifier.padding(end = 4.dp) + ) { + Icon( + imageVector = Icons.Outlined.Cancel, + contentDescription = stringResource(R.string.cancel), + tint = MaterialTheme.colorScheme.onSecondaryContainer + ) + } + }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = false, + onValueChange = { + key = it + component.resetCalculatedData() + }, + label = { + Text(stringResource(R.string.key)) } - - component.uri?.let { uri -> - PreferenceItem( - modifier = Modifier.padding(top = 16.dp), - title = context.getFilename(uri) - ?: stringResource(R.string.something_went_wrong), - onClick = null, - titleFontStyle = LocalTextStyle.current.copy( - lineHeight = 16.sp, - fontSize = 15.sp - ), - subtitle = component.uri?.let { - stringResource( - id = R.string.size, - readableByteCount( - it.fileSize(context) ?: 0L - ) + ) + } + AnimatedVisibility(visible = component.byteArray != null) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp) + .container( + shape = MaterialTheme.shapes.extraLarge, + color = MaterialTheme + .colorScheme + .surfaceContainerHighest, + resultPadding = 0.dp + ) + .padding(16.dp) + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = Icons.Rounded.CheckCircle, + contentDescription = null, + tint = Green, + modifier = Modifier + .size(36.dp) + .background( + color = MaterialTheme.colorScheme.surface, + shape = CircleShape ) - }, - startIcon = Icons.AutoMirrored.Rounded.InsertDriveFile + .border( + width = settingsState.borderWidth, + color = MaterialTheme.colorScheme.outlineVariant(), + shape = CircleShape + ) + .padding(4.dp) ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + stringResource(R.string.file_proceed), + fontSize = 17.sp, + fontWeight = FontWeight.Medium + ) + } + Text( + text = stringResource(R.string.store_file_desc), + fontSize = 13.sp, + color = LocalContentColor.current.copy(alpha = 0.7f), + lineHeight = 14.sp, + modifier = Modifier.padding(vertical = 16.dp) + ) + var name by rememberSaveable(component.byteArray) { + mutableStateOf( + if (component.isEncrypt) { + "enc-" + } else { + "dec-" + } + (component.uri?.let { + context.getFilename(it) + } ?: Random.nextInt()) + ) + } + RoundedTextField( + modifier = Modifier + .padding(top = 8.dp) + .container(shape = RoundedCornerShape(24.dp)) + .padding(8.dp), + value = name, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = false, + onValueChange = { name = it }, + label = { + Text(stringResource(R.string.filename)) + } + ) - RoundedTextField( - modifier = Modifier - .padding(top = 16.dp) - .container(shape = RoundedCornerShape(24.dp)) - .padding(8.dp), - value = key, - startIcon = { - EnhancedIconButton( - onClick = { - key = component.generateRandomPassword() - component.resetCalculatedData() - }, - modifier = Modifier.padding(start = 4.dp) - ) { - Icon( - imageVector = Icons.Rounded.Shuffle, - contentDescription = stringResource(R.string.shuffle), - tint = MaterialTheme.colorScheme.onSecondaryContainer - ) - } - }, - endIcon = { - EnhancedIconButton( - onClick = { - key = "" - component.resetCalculatedData() - }, - modifier = Modifier.padding(end = 4.dp) - ) { - Icon( - imageVector = Icons.Outlined.Cancel, - contentDescription = stringResource(R.string.cancel), - tint = MaterialTheme.colorScheme.onSecondaryContainer - ) + Row( + modifier = Modifier + .padding(top = 24.dp) + .fillMaxWidth() + ) { + EnhancedButton( + onClick = { + runCatching { + saveLauncher.launch(name) + }.onFailure { + essentials.showActivateFilesToast() } }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - singleLine = false, - onValueChange = { - key = it - component.resetCalculatedData() - }, - label = { - Text(stringResource(R.string.key)) - } - ) - } - AnimatedVisibility(visible = component.byteArray != null) { - Column( modifier = Modifier - .fillMaxWidth() - .padding(top = 24.dp) - .container( - shape = MaterialTheme.shapes.extraLarge, - color = MaterialTheme - .colorScheme - .surfaceContainerHighest, - resultPadding = 0.dp - ) - .padding(16.dp) + .padding(end = 8.dp) + .fillMaxWidth(0.5f) + .height(50.dp), + containerColor = MaterialTheme.colorScheme.secondaryContainer ) { - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { Icon( - imageVector = Icons.Rounded.CheckCircle, - contentDescription = null, - tint = Green, - modifier = Modifier - .size(36.dp) - .background( - color = MaterialTheme.colorScheme.surface, - shape = CircleShape - ) - .border( - width = settingsState.borderWidth, - color = MaterialTheme.colorScheme.outlineVariant(), - shape = CircleShape - ) - .padding(4.dp) - ) - Spacer(modifier = Modifier.width(16.dp)) - Text( - stringResource(R.string.file_proceed), - fontSize = 17.sp, - fontWeight = FontWeight.Medium + imageVector = Icons.Rounded.FileDownload, + contentDescription = null ) - } - Text( - text = stringResource(R.string.store_file_desc), - fontSize = 13.sp, - color = LocalContentColor.current.copy(alpha = 0.7f), - lineHeight = 14.sp, - modifier = Modifier.padding(vertical = 16.dp) - ) - var name by rememberSaveable(component.byteArray) { - mutableStateOf( - if (component.isEncrypt) { - "enc-" - } else { - "dec-" - } + (component.uri?.let { - context.getFilename(it) - } ?: Random.nextInt()) + Spacer(modifier = Modifier.width(8.dp)) + AutoSizeText( + text = stringResource(id = R.string.save), + maxLines = 1 ) } - RoundedTextField( - modifier = Modifier - .padding(top = 8.dp) - .container(shape = RoundedCornerShape(24.dp)) - .padding(8.dp), - value = name, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - singleLine = false, - onValueChange = { name = it }, - label = { - Text(stringResource(R.string.filename)) + } + EnhancedButton( + onClick = { + component.byteArray?.let { + component.shareFile( + it = it, + filename = name, + onComplete = showConfetti + ) } - ) - + }, + modifier = Modifier + .padding(start = 8.dp) + .fillMaxWidth() + .height(50.dp), + containerColor = MaterialTheme.colorScheme.secondaryContainer + ) { Row( - modifier = Modifier - .padding(top = 24.dp) - .fillMaxWidth() + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center ) { - EnhancedButton( - onClick = { - runCatching { - saveLauncher.launch(name) - }.onFailure { - essentials.showActivateFilesToast() - } - }, - modifier = Modifier - .padding(end = 8.dp) - .fillMaxWidth(0.5f) - .height(50.dp), - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Icon( - imageVector = Icons.Rounded.FileDownload, - contentDescription = null - ) - Spacer(modifier = Modifier.width(8.dp)) - AutoSizeText( - text = stringResource(id = R.string.save), - maxLines = 1 - ) - } - } - EnhancedButton( - onClick = { - component.byteArray?.let { - component.shareFile( - it = it, - filename = name, - onComplete = showConfetti - ) - } - }, - modifier = Modifier - .padding(start = 8.dp) - .fillMaxWidth() - .height(50.dp), - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Icon( - imageVector = Icons.Rounded.Share, - contentDescription = stringResource( - R.string.share - ) - ) - Spacer(modifier = Modifier.width(8.dp)) - AutoSizeText( - text = stringResource(id = R.string.share), - maxLines = 1 - ) - } - } + Icon( + imageVector = Icons.Rounded.Share, + contentDescription = stringResource( + R.string.share + ) + ) + Spacer(modifier = Modifier.width(8.dp)) + AutoSizeText( + text = stringResource(id = R.string.share), + maxLines = 1 + ) } } } } } + Spacer(Modifier.height(16.dp)) + DataSelector( + modifier = Modifier, + value = component.cipherType, + color = Color.Unspecified, + spanCount = 5, + selectedItemColor = MaterialTheme.colorScheme.secondary, + onValueChange = component::updateCipherType, + entries = CipherType.entries, + title = stringResource(R.string.algorithms), + titleIcon = Icons.Rounded.Key, + itemContentText = { + it.name + }, + initialExpanded = true + ) } }, imagePreview = {}, diff --git a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/screenLogic/CipherComponent.kt b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/screenLogic/CipherComponent.kt index b24afd81b0..05aae9e463 100644 --- a/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/screenLogic/CipherComponent.kt +++ b/feature/cipher/src/main/java/ru/tech/imageresizershrinker/feature/cipher/presentation/screenLogic/CipherComponent.kt @@ -29,6 +29,7 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.Job import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder import ru.tech.imageresizershrinker.core.domain.image.ShareProvider +import ru.tech.imageresizershrinker.core.domain.model.CipherType import ru.tech.imageresizershrinker.core.domain.saving.FileController import ru.tech.imageresizershrinker.core.domain.saving.model.SaveResult import ru.tech.imageresizershrinker.core.domain.utils.smartJob @@ -53,6 +54,9 @@ class CipherComponent @AssistedInject internal constructor( } } + private val _cipherType: MutableState = mutableStateOf(CipherType.entries.first()) + val cipherType: CipherType by _cipherType + private val _uri = mutableStateOf(null) val uri by _uri @@ -88,9 +92,21 @@ class CipherComponent @AssistedInject internal constructor( val file = onFileRequest(_uri.value!!) runCatching { if (isEncrypt) { - _byteArray.value = file?.let { cryptographyManager.encrypt(it, key) } + _byteArray.value = file?.let { + cryptographyManager.encrypt( + data = it, + key = key, + type = cipherType + ) + } } else { - _byteArray.value = file?.let { cryptographyManager.decrypt(it, key) } + _byteArray.value = file?.let { + cryptographyManager.decrypt( + data = it, + key = key, + type = cipherType + ) + } } }.exceptionOrNull().let { onComplete( @@ -103,6 +119,11 @@ class CipherComponent @AssistedInject internal constructor( } } + fun updateCipherType(type: CipherType) { + _cipherType.update { type } + resetCalculatedData() + } + fun setIsEncrypt(isEncrypt: Boolean) { _isEncrypt.value = isEncrypt resetCalculatedData() diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/AndroidSettingsManager.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/AndroidSettingsManager.kt index fb25e640e0..7d6f46ec43 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/AndroidSettingsManager.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/AndroidSettingsManager.kt @@ -38,8 +38,8 @@ import ru.tech.imageresizershrinker.core.domain.GlobalStorageName import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.ColorModel +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.PerformanceClass import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility import ru.tech.imageresizershrinker.core.settings.domain.SettingsManager @@ -620,7 +620,7 @@ internal class AndroidSettingsManager @Inject constructor( it[FAST_SETTINGS_SIDE] = side.ordinal } - override suspend fun setChecksumTypeForFilename(type: ChecksumType?) = edit { + override suspend fun setChecksumTypeForFilename(type: HashingType?) = edit { it[CHECKSUM_TYPE_FOR_FILENAME] = type?.digest ?: "" } diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/keys/MapToSettingsState.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/keys/MapToSettingsState.kt index 17c00c1c9c..e51cffd259 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/keys/MapToSettingsState.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/data/keys/MapToSettingsState.kt @@ -21,8 +21,8 @@ import androidx.datastore.preferences.core.Preferences import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.Preset import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.ColorModel +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility import ru.tech.imageresizershrinker.core.settings.domain.model.ColorHarmonizer import ru.tech.imageresizershrinker.core.settings.domain.model.CopyToClipboardMode @@ -186,7 +186,7 @@ internal fun Preferences.toSettingsState( settingGroupsInitialVisibility = this[SETTINGS_GROUP_VISIBILITY].toSettingGroupsInitialVisibility( default ), - checksumTypeForFilename = ChecksumType.fromString(this[CHECKSUM_TYPE_FOR_FILENAME]), + hashingTypeForFilename = HashingType.fromString(this[CHECKSUM_TYPE_FOR_FILENAME]), customFonts = this[CUSTOM_FONTS].toCustomFonts() ) diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddFileSizeSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddFileSizeSettingItem.kt index 17098aac76..9d66f7c419 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddFileSizeSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddFileSizeSettingItem.kt @@ -40,7 +40,7 @@ fun AddFileSizeSettingItem( PreferenceRowSwitch( shape = shape, modifier = modifier, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, startIcon = Icons.Outlined.ScaleUnbalanced, onClick = { onClick() diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddOriginalFilenameSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddOriginalFilenameSettingItem.kt index 1b6d18fd74..fb9db271a7 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddOriginalFilenameSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddOriginalFilenameSettingItem.kt @@ -48,7 +48,7 @@ fun AddOriginalFilenameSettingItem( val settingsState = LocalSettingsState.current PreferenceRowSwitch( shape = shape, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.picturePickerMode != PicturePickerMode.PhotoPicker && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.picturePickerMode != PicturePickerMode.PhotoPicker && settingsState.hashingTypeForFilename == null, modifier = modifier, startIcon = Icons.Outlined.Difference, onClick = { diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddTimestampToFilenameSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddTimestampToFilenameSettingItem.kt index 867082f1bf..14b1fb12af 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddTimestampToFilenameSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/AddTimestampToFilenameSettingItem.kt @@ -43,7 +43,7 @@ fun AddTimestampToFilenameSettingItem( onClick = { onClick() }, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, title = stringResource(R.string.add_timestamp), subtitle = stringResource(R.string.add_timestamp_sub), checked = settingsState.addTimestampToFilename, diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ChecksumAsFilenameSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ChecksumAsFilenameSettingItem.kt index 3d5cb1f91f..0597499005 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ChecksumAsFilenameSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ChecksumAsFilenameSettingItem.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.resources.R import ru.tech.imageresizershrinker.core.settings.presentation.provider.LocalSettingsState import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.DataSelector @@ -43,18 +43,18 @@ import ru.tech.imageresizershrinker.core.ui.widget.preferences.PreferenceRowSwit @Composable fun ChecksumAsFilenameSettingItem( - onValueChange: (ChecksumType?) -> Unit, + onValueChange: (HashingType?) -> Unit, shape: Shape = ContainerShapeDefaults.centerShape, modifier: Modifier = Modifier.padding(horizontal = 8.dp) ) { val settingsState = LocalSettingsState.current var checkedState by remember { - mutableStateOf(settingsState.checksumTypeForFilename != null) + mutableStateOf(settingsState.hashingTypeForFilename != null) } LaunchedEffect(checkedState) { onValueChange( if (checkedState) { - settingsState.checksumTypeForFilename ?: ChecksumType.entries.first() + settingsState.hashingTypeForFilename ?: HashingType.entries.first() } else { null } @@ -80,15 +80,15 @@ fun ChecksumAsFilenameSettingItem( DataSelector( modifier = Modifier .padding(top = 16.dp), - value = settingsState.checksumTypeForFilename ?: ChecksumType.entries.first(), + value = settingsState.hashingTypeForFilename ?: HashingType.entries.first(), onValueChange = onValueChange, - entries = ChecksumType.entries, + entries = HashingType.entries, color = MaterialTheme.colorScheme.surfaceContainerLowest, shape = shape, title = stringResource(R.string.algorithms), titleIcon = null, badgeContent = { - Text(ChecksumType.entries.size.toString()) + Text(HashingType.entries.size.toString()) }, itemContentText = { it.name diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenamePrefixSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenamePrefixSettingItem.kt index 87df77f0f6..63668e9b25 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenamePrefixSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenamePrefixSettingItem.kt @@ -63,7 +63,7 @@ fun FilenamePrefixSettingItem( onClick = { showChangeFilenameDialog = true }, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, title = stringResource(R.string.prefix), subtitle = (settingsState.filenamePrefix.takeIf { it.isNotEmpty() } ?: stringResource(R.string.empty)), diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenameSuffixSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenameSuffixSettingItem.kt index dc647445cf..ca85d42b93 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenameSuffixSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/FilenameSuffixSettingItem.kt @@ -63,7 +63,7 @@ fun FilenameSuffixSettingItem( onClick = { showChangeFilenameDialog = true }, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, title = stringResource(R.string.suffix), subtitle = (settingsState.filenameSuffix.takeIf { it.isNotEmpty() } ?: stringResource(R.string.empty)), diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/OverwriteFilesSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/OverwriteFilesSettingItem.kt index 17959ae6a5..dafcd4d745 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/OverwriteFilesSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/OverwriteFilesSettingItem.kt @@ -43,7 +43,7 @@ fun OverwriteFilesSettingItem( onClick = { onClick() }, - enabled = !settingsState.randomizeFilename && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && settingsState.hashingTypeForFilename == null, title = stringResource(R.string.overwrite_files), subtitle = stringResource(R.string.overwrite_files_sub), checked = settingsState.overwriteFiles, diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/RandomizeFilenameSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/RandomizeFilenameSettingItem.kt index 368343fc5f..add286fc01 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/RandomizeFilenameSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/RandomizeFilenameSettingItem.kt @@ -40,7 +40,7 @@ fun RandomizeFilenameSettingItem( PreferenceRowSwitch( shape = shape, modifier = modifier, - enabled = !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, onClick = { onClick() }, diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ReplaceSequenceNumberSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ReplaceSequenceNumberSettingItem.kt index 2c03fe2695..aadfd9e7ab 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ReplaceSequenceNumberSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/ReplaceSequenceNumberSettingItem.kt @@ -43,7 +43,7 @@ fun ReplaceSequenceNumberSettingItem( onClick = { onClick() }, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.hashingTypeForFilename == null, title = stringResource(R.string.replace_sequence_number), subtitle = stringResource(R.string.replace_sequence_number_sub), checked = settingsState.addSequenceNumber, diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/UseFormattedFilenameTimestampSettingItem.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/UseFormattedFilenameTimestampSettingItem.kt index da507424d4..bea177f07c 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/UseFormattedFilenameTimestampSettingItem.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/components/UseFormattedFilenameTimestampSettingItem.kt @@ -52,7 +52,7 @@ fun UseFormattedFilenameTimestampSettingItem( onClick = { onClick() }, - enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.addTimestampToFilename && settingsState.checksumTypeForFilename == null, + enabled = !settingsState.randomizeFilename && !settingsState.overwriteFiles && settingsState.addTimestampToFilename && settingsState.hashingTypeForFilename == null, onDisabledClick = { scope.launch { toastHostState.showToast( diff --git a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/screenLogic/SettingsComponent.kt b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/screenLogic/SettingsComponent.kt index 130870510b..60c318ad6e 100644 --- a/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/screenLogic/SettingsComponent.kt +++ b/feature/settings/src/main/java/ru/tech/imageresizershrinker/feature/settings/presentation/screenLogic/SettingsComponent.kt @@ -40,8 +40,8 @@ import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder import ru.tech.imageresizershrinker.core.domain.image.ImageGetter import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType -import ru.tech.imageresizershrinker.core.domain.model.ChecksumType import ru.tech.imageresizershrinker.core.domain.model.ColorModel +import ru.tech.imageresizershrinker.core.domain.model.HashingType import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility import ru.tech.imageresizershrinker.core.domain.resource.ResourceManager import ru.tech.imageresizershrinker.core.domain.saving.FileController @@ -752,7 +752,7 @@ class SettingsComponent @AssistedInject internal constructor( } } - fun setChecksumTypeForFilename(type: ChecksumType?) { + fun setChecksumTypeForFilename(type: HashingType?) { componentScope.launch { settingsManager.setChecksumTypeForFilename(type) }