mirror of
https://github.com/T8RIN/ImageToolbox.git
synced 2025-05-17 05:26:02 +08:00
Proposal of Audio cover export by #1742
This commit is contained in:
@ -1,5 +1,3 @@
|
||||
import dev.iurysouza.modulegraph.Theme
|
||||
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
@ -17,10 +15,36 @@ import dev.iurysouza.modulegraph.Theme
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
/** Added if needed to regenerate module graph
|
||||
|
||||
import dev.iurysouza.modulegraph.Theme
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.dev.iurysouza.modulegraph) apply true
|
||||
}
|
||||
|
||||
moduleGraphConfig {
|
||||
readmePath.set("./ARCHITECTURE.md")
|
||||
heading = "# 📐 Modules Graph"
|
||||
theme.set(
|
||||
Theme.BASE(
|
||||
mapOf(
|
||||
"primaryColor" to "#00381a",
|
||||
"primaryTextColor" to "#d4fcb1",
|
||||
"primaryBorderColor" to "#14b800",
|
||||
"lineColor" to "#15c400",
|
||||
"secondaryColor" to "#283b26",
|
||||
"tertiaryColor" to "#355238",
|
||||
"nodeTextColor" to "#e0ffd6",
|
||||
"edgeLabelBackground" to "#1a1a1a",
|
||||
"fontSize" to "28px"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
**/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
@ -46,24 +70,4 @@ buildscript {
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
||||
|
||||
moduleGraphConfig {
|
||||
readmePath.set("./ARCHITECTURE.md")
|
||||
heading = "# 📐 Modules Graph"
|
||||
theme.set(
|
||||
Theme.BASE(
|
||||
mapOf(
|
||||
"primaryColor" to "#00381a",
|
||||
"primaryTextColor" to "#d4fcb1",
|
||||
"primaryBorderColor" to "#14b800",
|
||||
"lineColor" to "#15c400",
|
||||
"secondaryColor" to "#283b26",
|
||||
"tertiaryColor" to "#355238",
|
||||
"nodeTextColor" to "#e0ffd6",
|
||||
"edgeLabelBackground" to "#1a1a1a",
|
||||
"fontSize" to "28px"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
@ -1602,4 +1602,6 @@
|
||||
<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>
|
||||
<string name="generated_barcode_will_be_here">Generated Barcode Will Be Here</string>
|
||||
<string name="audio_cover_extractor">Audio Cover Extractor</string>
|
||||
<string name="audio_cover_extractor_sub">Extract album cover images from audio files, most common formats are supported</string>
|
||||
</resources>
|
@ -199,7 +199,7 @@ object ContextUtils {
|
||||
onShowToast: (message: String, icon: ImageVector) -> Unit,
|
||||
onNavigate: (Screen) -> Unit,
|
||||
onGetUris: (List<Uri>) -> Unit,
|
||||
onHasExtraImageType: (String) -> Unit,
|
||||
onHasExtraImageType: (String) -> Unit, //TODO: Add normal sealed class instead of string
|
||||
isHasUris: Boolean,
|
||||
onWantGithubReview: () -> Unit,
|
||||
isOpenEditInsteadOfPreview: Boolean,
|
||||
@ -294,10 +294,17 @@ object ContextUtils {
|
||||
onHasExtraImageType(text)
|
||||
onGetUris(listOf())
|
||||
} else {
|
||||
val isAudio = intent.type?.startsWith("audio/") == true
|
||||
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
intent.parcelableArrayList<Uri>(Intent.EXTRA_STREAM)?.let {
|
||||
onNavigate(Screen.Zip(it))
|
||||
if (isAudio) {
|
||||
onHasExtraImageType("audio")
|
||||
onGetUris(it)
|
||||
} else {
|
||||
onNavigate(Screen.Zip(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +314,12 @@ object ContextUtils {
|
||||
onHasExtraImageType("$BackupFileExtension $it")
|
||||
return
|
||||
}
|
||||
onHasExtraImageType("file")
|
||||
if (isAudio) {
|
||||
onHasExtraImageType("audio")
|
||||
} else {
|
||||
onHasExtraImageType("file")
|
||||
}
|
||||
|
||||
onGetUris(listOf(it))
|
||||
}
|
||||
}
|
||||
@ -326,10 +338,20 @@ object ContextUtils {
|
||||
return
|
||||
}
|
||||
|
||||
onHasExtraImageType("file")
|
||||
if (isAudio) {
|
||||
onHasExtraImageType("audio")
|
||||
} else {
|
||||
onHasExtraImageType("file")
|
||||
}
|
||||
|
||||
onGetUris(uris)
|
||||
} else if (uris.isNotEmpty()) {
|
||||
onNavigate(Screen.Zip(uris))
|
||||
if (isAudio) {
|
||||
onHasExtraImageType("audio")
|
||||
onGetUris(uris)
|
||||
} else {
|
||||
onNavigate(Screen.Zip(uris))
|
||||
}
|
||||
} else {
|
||||
Unit
|
||||
}
|
||||
|
@ -726,6 +726,15 @@ sealed class Screen(
|
||||
subtitle = R.string.image_cutting_sub
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AudioCoverExtractor(
|
||||
val uris: List<Uri>? = null
|
||||
) : Screen(
|
||||
id = 39,
|
||||
title = R.string.audio_cover_extractor,
|
||||
subtitle = R.string.audio_cover_extractor_sub
|
||||
)
|
||||
|
||||
companion object : ScreenConstants by ScreenConstantsImpl
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ import androidx.compose.material.icons.outlined.Grain
|
||||
import androidx.compose.material.icons.outlined.Photo
|
||||
import androidx.compose.material.icons.outlined.PictureAsPdf
|
||||
import androidx.compose.material.icons.outlined.QrCode
|
||||
import androidx.compose.material.icons.rounded.Album
|
||||
import androidx.compose.material.icons.rounded.Compare
|
||||
import androidx.compose.material.icons.rounded.ContentCut
|
||||
import androidx.compose.material.icons.rounded.Tag
|
||||
@ -71,7 +72,51 @@ import ru.tech.imageresizershrinker.core.resources.icons.Stack
|
||||
import ru.tech.imageresizershrinker.core.resources.icons.Toolbox
|
||||
import ru.tech.imageresizershrinker.core.resources.icons.VectorPolyline
|
||||
import ru.tech.imageresizershrinker.core.resources.icons.WebpBox
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.*
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ApngTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.AudioCoverExtractor
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Base64Tools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ChecksumTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Cipher
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.CollageMaker
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ColorTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Compare
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Crop
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.DeleteExif
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.DocumentScanner
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Draw
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.EasterEgg
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.EditExif
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.EraseBackground
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Filter
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.FormatConversion
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.GeneratePalette
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.GifTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.GradientMaker
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ImageCutter
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ImagePreview
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ImageSplitting
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ImageStacking
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ImageStitching
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.JxlTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.LibrariesInfo
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.LimitResize
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.LoadNetImage
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Main
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.MarkupLayers
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.MeshGradients
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.NoiseGeneration
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.PdfTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.PickColorFromImage
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.RecognizeText
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ResizeAndConvert
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.ScanQrCode
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Settings
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.SingleEdit
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.SvgMaker
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Watermarking
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.WebpTools
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.WeightResize
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen.Zip
|
||||
import android.net.Uri as AndroidUri
|
||||
|
||||
internal fun Screen.isBetaFeature(): Boolean = when (this) {
|
||||
@ -82,7 +127,7 @@ internal fun Screen.isBetaFeature(): Boolean = when (this) {
|
||||
internal fun Screen.simpleName(): String? = when (this) {
|
||||
is ApngTools -> "APNG_Tools"
|
||||
is Cipher -> "Cipher"
|
||||
is Screen.Compare -> "Compare"
|
||||
is Compare -> "Compare"
|
||||
is Crop -> "Crop"
|
||||
is DeleteExif -> "Delete_Exif"
|
||||
is Draw -> "Draw"
|
||||
@ -109,7 +154,7 @@ internal fun Screen.simpleName(): String? = when (this) {
|
||||
is Zip -> "Zip"
|
||||
is SvgMaker -> "Svg"
|
||||
is FormatConversion -> "Convert"
|
||||
is Screen.DocumentScanner -> "Document_Scanner"
|
||||
is DocumentScanner -> "Document_Scanner"
|
||||
is ScanQrCode -> "QR_Code"
|
||||
is ImageStacking -> "Image_Stacking"
|
||||
is ImageSplitting -> "Image_Splitting"
|
||||
@ -124,6 +169,7 @@ internal fun Screen.simpleName(): String? = when (this) {
|
||||
is MeshGradients -> "Mesh_Gradients"
|
||||
is EditExif -> "Edit_EXIF"
|
||||
is ImageCutter -> "Image_Cutting"
|
||||
is AudioCoverExtractor -> "Audio_Cover_Extractor"
|
||||
}
|
||||
|
||||
internal fun Screen.icon(): ImageVector? = when (this) {
|
||||
@ -136,7 +182,7 @@ internal fun Screen.icon(): ImageVector? = when (this) {
|
||||
is SingleEdit -> Icons.Outlined.ImageEdit
|
||||
is ApngTools -> Icons.Rounded.ApngBox
|
||||
is Cipher -> Icons.Outlined.Encrypted
|
||||
is Screen.Compare -> Icons.Rounded.Compare
|
||||
is Compare -> Icons.Rounded.Compare
|
||||
is Crop -> Icons.Rounded.CropSmall
|
||||
is DeleteExif -> Icons.Outlined.Exif
|
||||
is Draw -> Icons.Outlined.Draw
|
||||
@ -159,7 +205,7 @@ internal fun Screen.icon(): ImageVector? = when (this) {
|
||||
is Zip -> Icons.Outlined.FolderZip
|
||||
is SvgMaker -> Icons.Outlined.VectorPolyline
|
||||
is FormatConversion -> Icons.Outlined.ImageConvert
|
||||
is Screen.DocumentScanner -> Icons.Outlined.DocumentScanner
|
||||
is DocumentScanner -> Icons.Outlined.DocumentScanner
|
||||
is ScanQrCode -> Icons.Outlined.QrCode
|
||||
is ImageStacking -> Icons.Outlined.ImageOverlay
|
||||
is ImageSplitting -> Icons.Outlined.SplitAlt
|
||||
@ -172,6 +218,7 @@ internal fun Screen.icon(): ImageVector? = when (this) {
|
||||
is ChecksumTools -> Icons.Rounded.Tag
|
||||
is EditExif -> Icons.Outlined.ExifEdit
|
||||
is ImageCutter -> Icons.Rounded.ContentCut
|
||||
is AudioCoverExtractor -> Icons.Rounded.Album
|
||||
}
|
||||
|
||||
internal object UriSerializer : KSerializer<AndroidUri> {
|
||||
@ -261,7 +308,8 @@ internal object ScreenConstantsImpl : ScreenConstants {
|
||||
Zip(),
|
||||
JxlTools(),
|
||||
ApngTools(),
|
||||
WebpTools()
|
||||
WebpTools(),
|
||||
AudioCoverExtractor()
|
||||
),
|
||||
title = R.string.tools,
|
||||
selectedIcon = Icons.Rounded.Toolbox,
|
||||
@ -274,5 +322,5 @@ internal object ScreenConstantsImpl : ScreenConstants {
|
||||
typedEntries.flatMap { it.entries }.sortedBy { it.id }
|
||||
}
|
||||
|
||||
override val FEATURES_COUNT = 66
|
||||
override val FEATURES_COUNT = 67
|
||||
}
|
@ -35,7 +35,7 @@ import java.util.Locale
|
||||
|
||||
@Composable
|
||||
internal fun List<Uri>.screenList(
|
||||
extraImageType: String?
|
||||
extraImageType: String? //TODO: Add normal sealed class instead of string
|
||||
): State<List<Screen>> {
|
||||
val uris = this
|
||||
val context = LocalContext.current
|
||||
@ -59,6 +59,17 @@ internal fun List<Uri>.screenList(
|
||||
)
|
||||
}
|
||||
}
|
||||
val audioAvailableScreens by remember(uris) {
|
||||
derivedStateOf {
|
||||
listOf(
|
||||
Screen.AudioCoverExtractor(uris)
|
||||
) + if (uris.size > 1) {
|
||||
filesAvailableScreens
|
||||
} else {
|
||||
listOf(Screen.Zip(uris))
|
||||
}
|
||||
}
|
||||
}
|
||||
val gifAvailableScreens by remember(uris) {
|
||||
derivedStateOf {
|
||||
listOf(
|
||||
@ -272,6 +283,7 @@ internal fun List<Uri>.screenList(
|
||||
) {
|
||||
derivedStateOf {
|
||||
when {
|
||||
extraImageType == "audio" -> audioAvailableScreens
|
||||
extraImageType == "pdf" -> pdfAvailableScreens
|
||||
extraImageType == "gif" -> gifAvailableScreens
|
||||
extraImageType == "file" -> filesAvailableScreens
|
||||
|
1
feature/audio-cover-extractor/.gitignore
vendored
Normal file
1
feature/audio-cover-extractor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
30
feature/audio-cover-extractor/build.gradle.kts
Normal file
30
feature/audio-cover-extractor/build.gradle.kts
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 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>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.image.toolbox.library)
|
||||
alias(libs.plugins.image.toolbox.feature)
|
||||
alias(libs.plugins.image.toolbox.hilt)
|
||||
alias(libs.plugins.image.toolbox.compose)
|
||||
}
|
||||
|
||||
android.namespace = "ru.tech.imageresizershrinker.feature.audio_cover_extractor"
|
||||
|
||||
dependencies {
|
||||
implementation(libs.ffmpeg.metadata.retriever.core)
|
||||
implementation(libs.ffmpeg.metadata.retriever.native)
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.audio_cover_extractor.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import androidx.core.net.toUri
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ImageCompressor
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ImageGetter
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ShareProvider
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.ImageFormat
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.Quality
|
||||
import ru.tech.imageresizershrinker.core.domain.resource.ResourceManager
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.domain.AudioCoverRetriever
|
||||
import wseemann.media.FFmpegMediaMetadataRetriever
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class AndroidAudioCoverRetriever @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val imageCompressor: ImageCompressor<Bitmap>,
|
||||
private val shareProvider: ShareProvider<Bitmap>,
|
||||
private val imageGetter: ImageGetter<Bitmap, ExifInterface>,
|
||||
dispatchersHolder: DispatchersHolder,
|
||||
resourceManager: ResourceManager
|
||||
) : AudioCoverRetriever,
|
||||
DispatchersHolder by dispatchersHolder,
|
||||
ResourceManager by resourceManager {
|
||||
|
||||
override suspend fun loadCover(
|
||||
audioUri: String
|
||||
): Result<String> {
|
||||
val pictureData = FFmpegMediaMetadataRetriever().apply {
|
||||
setDataSource(
|
||||
context,
|
||||
audioUri.toUri()
|
||||
)
|
||||
}.embeddedPicture
|
||||
|
||||
return imageGetter.getImage(
|
||||
data = pictureData,
|
||||
originalSize = true
|
||||
)?.let { bitmap ->
|
||||
shareProvider.cacheData(
|
||||
writeData = {
|
||||
it.writeBytes(
|
||||
imageCompressor.compress(
|
||||
image = bitmap,
|
||||
imageFormat = ImageFormat.Png.Lossless,
|
||||
quality = Quality.Base()
|
||||
)
|
||||
)
|
||||
},
|
||||
filename = "${audioUri.substringBeforeLast('.')}.png"
|
||||
)?.let(Result.Companion::success)
|
||||
} ?: Result.failure(NullPointerException(getString(R.string.no_image)))
|
||||
}
|
||||
|
||||
override suspend fun loadCover(
|
||||
audioData: ByteArray
|
||||
): Result<String> {
|
||||
return loadCover(
|
||||
shareProvider.cacheData(
|
||||
writeData = {
|
||||
it.writeBytes(audioData)
|
||||
},
|
||||
filename = "Audio_data_${System.currentTimeMillis()}.mp3"
|
||||
)
|
||||
?: return Result.failure(NullPointerException(getString(R.string.filename_is_not_set)))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.audio_cover_extractor.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.data.AndroidAudioCoverRetriever
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.domain.AudioCoverRetriever
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface AudioCoverExtractorModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun extractor(
|
||||
impl: AndroidAudioCoverRetriever
|
||||
): AudioCoverRetriever
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.audio_cover_extractor.domain
|
||||
|
||||
interface AudioCoverRetriever {
|
||||
|
||||
suspend fun loadCover(
|
||||
audioUri: String
|
||||
): Result<String>
|
||||
|
||||
suspend fun loadCover(
|
||||
audioData: ByteArray
|
||||
): Result<String>
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.audio_cover_extractor.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.ui.screenLogic.AudioCoverExtractorComponent
|
||||
|
||||
@Composable
|
||||
fun AudioCoverExtractorContent(
|
||||
component: AudioCoverExtractorComponent
|
||||
) {
|
||||
|
||||
}
|
@ -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.audio_cover_extractor.ui.components
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
data class AudioWithCover(
|
||||
val audioUri: Uri,
|
||||
val imageCoverUri: Uri?,
|
||||
val isLoading: Boolean
|
||||
)
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.audio_cover_extractor.ui.screenLogic
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.BaseComponent
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.domain.AudioCoverRetriever
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.ui.components.AudioWithCover
|
||||
|
||||
class AudioCoverExtractorComponent @AssistedInject constructor(
|
||||
@Assisted componentContext: ComponentContext,
|
||||
@Assisted val initialUris: List<Uri>?,
|
||||
@Assisted val onGoBack: () -> Unit,
|
||||
@Assisted val onNavigate: (Screen) -> Unit,
|
||||
private val audioCoverRetriever: AudioCoverRetriever,
|
||||
dispatchersHolder: DispatchersHolder
|
||||
) : BaseComponent(dispatchersHolder, componentContext) {
|
||||
|
||||
init {
|
||||
debounce {
|
||||
initialUris?.let(::updateCovers)
|
||||
}
|
||||
}
|
||||
|
||||
private val _covers: MutableStateFlow<List<AudioWithCover>> = MutableStateFlow(emptyList())
|
||||
val covers: StateFlow<List<AudioWithCover>> = _covers.asStateFlow()
|
||||
|
||||
|
||||
fun updateCovers(uris: List<Uri>) {
|
||||
val audioUris = uris.distinct()
|
||||
|
||||
componentScope.launch {
|
||||
_covers.update {
|
||||
audioUris.map {
|
||||
AudioWithCover(
|
||||
audioUri = it,
|
||||
imageCoverUri = null,
|
||||
isLoading = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val newCovers = audioUris.map { audioUri ->
|
||||
async {
|
||||
val coverUri = audioCoverRetriever.loadCover(audioUri.toString()).getOrNull()
|
||||
|
||||
val newCover = AudioWithCover(
|
||||
audioUri = audioUri,
|
||||
imageCoverUri = coverUri?.toUri(),
|
||||
isLoading = false
|
||||
)
|
||||
|
||||
_covers.update { covers ->
|
||||
covers.toMutableList().apply {
|
||||
val index = indexOfFirst { it.audioUri == audioUri }.takeIf { it >= 0 }
|
||||
?: return@update covers
|
||||
|
||||
set(index, newCover)
|
||||
}
|
||||
}
|
||||
|
||||
newCover
|
||||
}
|
||||
}
|
||||
|
||||
_covers.update {
|
||||
newCovers.awaitAll().filter { it.imageCoverUri != null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
operator fun invoke(
|
||||
componentContext: ComponentContext,
|
||||
initialUris: List<Uri>?,
|
||||
onGoBack: () -> Unit,
|
||||
onNavigate: (Screen) -> Unit,
|
||||
): AudioCoverExtractorComponent
|
||||
}
|
||||
}
|
@ -31,9 +31,7 @@ import coil3.request.ImageRequest
|
||||
import coil3.size.Size
|
||||
import coil3.toBitmap
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.tech.imageresizershrinker.core.data.utils.aspectRatio
|
||||
import ru.tech.imageresizershrinker.core.data.utils.getSuitableConfig
|
||||
@ -108,17 +106,16 @@ internal class AndroidPdfManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertPdfToImages(
|
||||
override suspend fun convertPdfToImages(
|
||||
pdfUri: String,
|
||||
pages: List<Int>?,
|
||||
preset: Preset.Percentage,
|
||||
onGetPagesCount: suspend (Int) -> Unit,
|
||||
onProgressChange: suspend (Int, Bitmap) -> Unit,
|
||||
onComplete: suspend () -> Unit
|
||||
) = CoroutineScope(ioDispatcher).launch {
|
||||
): Unit = withContext(ioDispatcher) {
|
||||
context.contentResolver.openFileDescriptor(
|
||||
pdfUri.toUri(),
|
||||
"r"
|
||||
pdfUri.toUri(), "r"
|
||||
)?.use { fileDescriptor ->
|
||||
withContext(defaultDispatcher) {
|
||||
val pdfRenderer = PdfRenderer(fileDescriptor)
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package ru.tech.imageresizershrinker.feature.pdf_tools.domain
|
||||
|
||||
import kotlinx.coroutines.Job
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.Preset
|
||||
import ru.tech.imageresizershrinker.core.domain.model.IntegerSize
|
||||
|
||||
@ -34,13 +33,13 @@ interface PdfManager<I> {
|
||||
preset: Preset.Percentage
|
||||
): ByteArray
|
||||
|
||||
fun convertPdfToImages(
|
||||
suspend fun convertPdfToImages(
|
||||
pdfUri: String,
|
||||
pages: List<Int>?,
|
||||
preset: Preset.Percentage,
|
||||
onGetPagesCount: suspend (Int) -> Unit,
|
||||
onProgressChange: suspend (Int, I) -> Unit,
|
||||
onComplete: suspend () -> Unit = {}
|
||||
): Job
|
||||
)
|
||||
|
||||
}
|
@ -215,44 +215,46 @@ class PdfToolsComponent @AssistedInject internal constructor(
|
||||
_done.value = 0
|
||||
_left.value = 1
|
||||
val results = mutableListOf<SaveResult>()
|
||||
savingJob = pdfManager.convertPdfToImages(
|
||||
pdfUri = _pdfToImageState.value?.uri.toString(),
|
||||
pages = _pdfToImageState.value?.selectedPages,
|
||||
preset = presetSelected,
|
||||
onProgressChange = { _, bitmap ->
|
||||
val imageInfo = imageTransformer.applyPresetBy(
|
||||
image = bitmap,
|
||||
preset = _presetSelected.value,
|
||||
currentInfo = imageInfo
|
||||
)
|
||||
|
||||
results.add(
|
||||
fileController.save(
|
||||
saveTarget = ImageSaveTarget(
|
||||
imageInfo = imageInfo,
|
||||
metadata = null,
|
||||
originalUri = _pdfToImageState.value?.uri.toString(),
|
||||
sequenceNumber = _done.value + 1,
|
||||
data = imageCompressor.compressAndTransform(
|
||||
image = bitmap,
|
||||
imageInfo = imageInfo
|
||||
)
|
||||
),
|
||||
keepOriginalMetadata = false,
|
||||
oneTimeSaveLocationUri = oneTimeSaveLocationUri
|
||||
savingJob = componentScope.launch {
|
||||
pdfManager.convertPdfToImages(
|
||||
pdfUri = _pdfToImageState.value?.uri.toString(),
|
||||
pages = _pdfToImageState.value?.selectedPages,
|
||||
preset = presetSelected,
|
||||
onProgressChange = { _, bitmap ->
|
||||
val imageInfo = imageTransformer.applyPresetBy(
|
||||
image = bitmap,
|
||||
preset = _presetSelected.value,
|
||||
currentInfo = imageInfo
|
||||
)
|
||||
)
|
||||
_done.value += 1
|
||||
},
|
||||
onGetPagesCount = { size ->
|
||||
_left.update { size }
|
||||
_isSaving.value = true
|
||||
},
|
||||
onComplete = {
|
||||
_isSaving.value = false
|
||||
onComplete(results.onSuccess(::registerSave))
|
||||
}
|
||||
)
|
||||
|
||||
results.add(
|
||||
fileController.save(
|
||||
saveTarget = ImageSaveTarget(
|
||||
imageInfo = imageInfo,
|
||||
metadata = null,
|
||||
originalUri = _pdfToImageState.value?.uri.toString(),
|
||||
sequenceNumber = _done.value + 1,
|
||||
data = imageCompressor.compressAndTransform(
|
||||
image = bitmap,
|
||||
imageInfo = imageInfo
|
||||
)
|
||||
),
|
||||
keepOriginalMetadata = false,
|
||||
oneTimeSaveLocationUri = oneTimeSaveLocationUri
|
||||
)
|
||||
)
|
||||
_done.value += 1
|
||||
},
|
||||
onGetPagesCount = { size ->
|
||||
_left.update { size }
|
||||
_isSaving.value = true
|
||||
},
|
||||
onComplete = {
|
||||
_isSaving.value = false
|
||||
onComplete(results.onSuccess(::registerSave))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun convertImagesToPdf(onComplete: () -> Unit) {
|
||||
@ -315,39 +317,41 @@ class PdfToolsComponent @AssistedInject internal constructor(
|
||||
_left.value = 1
|
||||
_isSaving.value = false
|
||||
val uris: MutableList<String?> = mutableListOf()
|
||||
savingJob = pdfManager.convertPdfToImages(
|
||||
pdfUri = _pdfToImageState.value?.uri.toString(),
|
||||
pages = _pdfToImageState.value?.selectedPages,
|
||||
onProgressChange = { _, bitmap ->
|
||||
imageInfo.copy(
|
||||
originalUri = _pdfToImageState.value?.uri?.toString()
|
||||
).let {
|
||||
imageTransformer.applyPresetBy(
|
||||
image = bitmap,
|
||||
preset = _presetSelected.value,
|
||||
currentInfo = it
|
||||
)
|
||||
}.apply {
|
||||
uris.add(
|
||||
shareProvider.cacheImage(
|
||||
imageInfo = this,
|
||||
image = bitmap
|
||||
savingJob = componentScope.launch {
|
||||
pdfManager.convertPdfToImages(
|
||||
pdfUri = _pdfToImageState.value?.uri.toString(),
|
||||
pages = _pdfToImageState.value?.selectedPages,
|
||||
onProgressChange = { _, bitmap ->
|
||||
imageInfo.copy(
|
||||
originalUri = _pdfToImageState.value?.uri?.toString()
|
||||
).let {
|
||||
imageTransformer.applyPresetBy(
|
||||
image = bitmap,
|
||||
preset = _presetSelected.value,
|
||||
currentInfo = it
|
||||
)
|
||||
)
|
||||
}.apply {
|
||||
uris.add(
|
||||
shareProvider.cacheImage(
|
||||
imageInfo = this,
|
||||
image = bitmap
|
||||
)
|
||||
)
|
||||
}
|
||||
_done.value += 1
|
||||
},
|
||||
preset = presetSelected,
|
||||
onGetPagesCount = { size ->
|
||||
_left.update { size }
|
||||
_isSaving.value = true
|
||||
},
|
||||
onComplete = {
|
||||
_isSaving.value = false
|
||||
shareProvider.shareUris(uris.filterNotNull())
|
||||
onComplete()
|
||||
}
|
||||
_done.value += 1
|
||||
},
|
||||
preset = presetSelected,
|
||||
onGetPagesCount = { size ->
|
||||
_left.update { size }
|
||||
_isSaving.value = true
|
||||
},
|
||||
onComplete = {
|
||||
_isSaving.value = false
|
||||
shareProvider.shareUris(uris.filterNotNull())
|
||||
onComplete()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is Screen.PdfTools.Type.Preview -> {
|
||||
|
@ -69,4 +69,5 @@ dependencies {
|
||||
implementation(projects.feature.meshGradients)
|
||||
implementation(projects.feature.editExif)
|
||||
implementation(projects.feature.imageCutting)
|
||||
implementation(projects.feature.audioCoverExtractor)
|
||||
}
|
@ -22,6 +22,7 @@ import ru.tech.imageresizershrinker.collage_maker.presentation.screenLogic.Colla
|
||||
import ru.tech.imageresizershrinker.color_tools.presentation.screenLogic.ColorToolsComponent
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen
|
||||
import ru.tech.imageresizershrinker.feature.apng_tools.presentation.screenLogic.ApngToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.ui.screenLogic.AudioCoverExtractorComponent
|
||||
import ru.tech.imageresizershrinker.feature.base64_tools.presentation.screenLogic.Base64ToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.checksum_tools.presentation.screenLogic.ChecksumToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.cipher.presentation.screenLogic.CipherComponent
|
||||
@ -53,6 +54,7 @@ import ru.tech.imageresizershrinker.feature.pick_color.presentation.screenLogic.
|
||||
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.ApngTools
|
||||
import ru.tech.imageresizershrinker.feature.root.presentation.components.navigation.NavigationChild.AudioCoverExtractor
|
||||
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
|
||||
@ -154,7 +156,8 @@ internal class ChildProvider @Inject constructor(
|
||||
private val checksumToolsComponentFactory: ChecksumToolsComponent.Factory,
|
||||
private val meshGradientsComponentFactory: MeshGradientsComponent.Factory,
|
||||
private val editExifComponentFactory: EditExifComponent.Factory,
|
||||
private val imageCutterComponentFactory: ImageCutterComponent.Factory
|
||||
private val imageCutterComponentFactory: ImageCutterComponent.Factory,
|
||||
private val audioCoverExtractorComponentFactory: AudioCoverExtractorComponent.Factory
|
||||
) {
|
||||
fun RootComponent.createChild(
|
||||
config: Screen,
|
||||
@ -541,5 +544,14 @@ internal class ChildProvider @Inject constructor(
|
||||
onNavigate = ::navigateTo
|
||||
)
|
||||
)
|
||||
|
||||
is Screen.AudioCoverExtractor -> AudioCoverExtractor(
|
||||
audioCoverExtractorComponentFactory(
|
||||
componentContext = componentContext,
|
||||
initialUris = config.uris,
|
||||
onGoBack = ::navigateBack,
|
||||
onNavigate = ::navigateTo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ import ru.tech.imageresizershrinker.color_tools.presentation.ColorToolsContent
|
||||
import ru.tech.imageresizershrinker.color_tools.presentation.screenLogic.ColorToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.apng_tools.presentation.ApngToolsContent
|
||||
import ru.tech.imageresizershrinker.feature.apng_tools.presentation.screenLogic.ApngToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.ui.AudioCoverExtractorContent
|
||||
import ru.tech.imageresizershrinker.feature.audio_cover_extractor.ui.screenLogic.AudioCoverExtractorComponent
|
||||
import ru.tech.imageresizershrinker.feature.base64_tools.presentation.Base64ToolsContent
|
||||
import ru.tech.imageresizershrinker.feature.base64_tools.presentation.screenLogic.Base64ToolsComponent
|
||||
import ru.tech.imageresizershrinker.feature.checksum_tools.presentation.ChecksumToolsContent
|
||||
@ -108,230 +110,237 @@ import ru.tech.imageresizershrinker.noise_generation.presentation.NoiseGeneratio
|
||||
import ru.tech.imageresizershrinker.noise_generation.presentation.screenLogic.NoiseGenerationComponent
|
||||
|
||||
|
||||
internal sealed class NavigationChild {
|
||||
internal sealed interface NavigationChild {
|
||||
|
||||
@Composable
|
||||
abstract fun Content()
|
||||
fun Content()
|
||||
|
||||
|
||||
class ApngTools(val component: ApngToolsComponent) : NavigationChild() {
|
||||
class ApngTools(private val component: ApngToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ApngToolsContent(component)
|
||||
}
|
||||
|
||||
class Cipher(val component: CipherComponent) : NavigationChild() {
|
||||
class Cipher(private val component: CipherComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = CipherContent(component)
|
||||
}
|
||||
|
||||
class CollageMaker(val component: CollageMakerComponent) : NavigationChild() {
|
||||
class CollageMaker(private val component: CollageMakerComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = CollageMakerContent(component)
|
||||
}
|
||||
|
||||
class ColorTools(val component: ColorToolsComponent) : NavigationChild() {
|
||||
class ColorTools(private val component: ColorToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ColorToolsContent(component)
|
||||
}
|
||||
|
||||
class Compare(val component: CompareComponent) : NavigationChild() {
|
||||
class Compare(private val component: CompareComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = CompareContent(component)
|
||||
}
|
||||
|
||||
class Crop(val component: CropComponent) : NavigationChild() {
|
||||
class Crop(private val component: CropComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = CropContent(component)
|
||||
}
|
||||
|
||||
class DeleteExif(val component: DeleteExifComponent) : NavigationChild() {
|
||||
class DeleteExif(private val component: DeleteExifComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = DeleteExifContent(component)
|
||||
}
|
||||
|
||||
class DocumentScanner(val component: DocumentScannerComponent) : NavigationChild() {
|
||||
class DocumentScanner(private val component: DocumentScannerComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = DocumentScannerContent(component)
|
||||
}
|
||||
|
||||
class Draw(val component: DrawComponent) : NavigationChild() {
|
||||
class Draw(private val component: DrawComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = DrawContent(component)
|
||||
}
|
||||
|
||||
class EasterEgg(val component: EasterEggComponent) : NavigationChild() {
|
||||
class EasterEgg(private val component: EasterEggComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = EasterEggContent(component)
|
||||
}
|
||||
|
||||
class EraseBackground(val component: EraseBackgroundComponent) : NavigationChild() {
|
||||
class EraseBackground(private val component: EraseBackgroundComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = EraseBackgroundContent(component)
|
||||
}
|
||||
|
||||
class Filter(val component: FiltersComponent) : NavigationChild() {
|
||||
class Filter(private val component: FiltersComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = FiltersContent(component)
|
||||
}
|
||||
|
||||
class FormatConversion(val component: FormatConversionComponent) : NavigationChild() {
|
||||
class FormatConversion(private val component: FormatConversionComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = FormatConversionContent(component)
|
||||
}
|
||||
|
||||
class GeneratePalette(val component: GeneratePaletteComponent) : NavigationChild() {
|
||||
class GeneratePalette(private val component: GeneratePaletteComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = GeneratePaletteContent(component)
|
||||
}
|
||||
|
||||
class GifTools(val component: GifToolsComponent) : NavigationChild() {
|
||||
class GifTools(private val component: GifToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = GifToolsContent(component)
|
||||
}
|
||||
|
||||
class GradientMaker(val component: GradientMakerComponent) : NavigationChild() {
|
||||
class GradientMaker(private val component: GradientMakerComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = GradientMakerContent(component)
|
||||
}
|
||||
|
||||
class ImagePreview(val component: ImagePreviewComponent) : NavigationChild() {
|
||||
class ImagePreview(private val component: ImagePreviewComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ImagePreviewContent(component)
|
||||
}
|
||||
|
||||
class ImageSplitting(val component: ImageSplitterComponent) : NavigationChild() {
|
||||
class ImageSplitting(private val component: ImageSplitterComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ImageSplitterContent(component)
|
||||
}
|
||||
|
||||
class ImageStacking(val component: ImageStackingComponent) : NavigationChild() {
|
||||
class ImageStacking(private val component: ImageStackingComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ImageStackingContent(component)
|
||||
}
|
||||
|
||||
class ImageStitching(val component: ImageStitchingComponent) : NavigationChild() {
|
||||
class ImageStitching(private val component: ImageStitchingComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ImageStitchingContent(component)
|
||||
}
|
||||
|
||||
class JxlTools(val component: JxlToolsComponent) : NavigationChild() {
|
||||
class JxlTools(private val component: JxlToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = JxlToolsContent(component)
|
||||
}
|
||||
|
||||
class LimitResize(val component: LimitsResizeComponent) : NavigationChild() {
|
||||
class LimitResize(private val component: LimitsResizeComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = LimitsResizeContent(component)
|
||||
}
|
||||
|
||||
class LoadNetImage(val component: LoadNetImageComponent) : NavigationChild() {
|
||||
class LoadNetImage(private val component: LoadNetImageComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = LoadNetImageContent(component)
|
||||
}
|
||||
|
||||
class Main(val component: MainComponent) : NavigationChild() {
|
||||
class Main(private val component: MainComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = MainContent(component)
|
||||
}
|
||||
|
||||
class NoiseGeneration(val component: NoiseGenerationComponent) : NavigationChild() {
|
||||
class NoiseGeneration(private val component: NoiseGenerationComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = NoiseGenerationContent(component)
|
||||
}
|
||||
|
||||
class PdfTools(val component: PdfToolsComponent) : NavigationChild() {
|
||||
class PdfTools(private val component: PdfToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = PdfToolsContent(component)
|
||||
}
|
||||
|
||||
class PickColorFromImage(val component: PickColorFromImageComponent) : NavigationChild() {
|
||||
class PickColorFromImage(private val component: PickColorFromImageComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = PickColorFromImageContent(component)
|
||||
}
|
||||
|
||||
class RecognizeText(val component: RecognizeTextComponent) : NavigationChild() {
|
||||
class RecognizeText(private val component: RecognizeTextComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = RecognizeTextContent(component)
|
||||
}
|
||||
|
||||
class ResizeAndConvert(val component: ResizeAndConvertComponent) : NavigationChild() {
|
||||
class ResizeAndConvert(private val component: ResizeAndConvertComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ResizeAndConvertContent(component)
|
||||
}
|
||||
|
||||
class ScanQrCode(val component: ScanQrCodeComponent) : NavigationChild() {
|
||||
class ScanQrCode(private val component: ScanQrCodeComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ScanQrCodeContent(component)
|
||||
}
|
||||
|
||||
class Settings(val component: SettingsComponent) : NavigationChild() {
|
||||
class Settings(private val component: SettingsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = SettingsContent(component)
|
||||
}
|
||||
|
||||
class SingleEdit(val component: SingleEditComponent) : NavigationChild() {
|
||||
class SingleEdit(private val component: SingleEditComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = SingleEditContent(component)
|
||||
}
|
||||
|
||||
class SvgMaker(val component: SvgMakerComponent) : NavigationChild() {
|
||||
class SvgMaker(private val component: SvgMakerComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = SvgMakerContent(component)
|
||||
}
|
||||
|
||||
class Watermarking(val component: WatermarkingComponent) : NavigationChild() {
|
||||
class Watermarking(private val component: WatermarkingComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = WatermarkingContent(component)
|
||||
}
|
||||
|
||||
class WebpTools(val component: WebpToolsComponent) : NavigationChild() {
|
||||
class WebpTools(private val component: WebpToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = WebpToolsContent(component)
|
||||
}
|
||||
|
||||
class WeightResize(val component: WeightResizeComponent) : NavigationChild() {
|
||||
class WeightResize(private val component: WeightResizeComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = WeightResizeContent(component)
|
||||
}
|
||||
|
||||
class Zip(val component: ZipComponent) : NavigationChild() {
|
||||
class Zip(private val component: ZipComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ZipContent(component)
|
||||
}
|
||||
|
||||
class LibrariesInfo(val component: LibrariesInfoComponent) : NavigationChild() {
|
||||
class LibrariesInfo(private val component: LibrariesInfoComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = LibrariesInfoContent(component)
|
||||
}
|
||||
|
||||
class MarkupLayers(val component: MarkupLayersComponent) : NavigationChild() {
|
||||
class MarkupLayers(private val component: MarkupLayersComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = MarkupLayersContent(component)
|
||||
}
|
||||
|
||||
class Base64Tools(val component: Base64ToolsComponent) : NavigationChild() {
|
||||
class Base64Tools(private val component: Base64ToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = Base64ToolsContent(component)
|
||||
}
|
||||
|
||||
class ChecksumTools(val component: ChecksumToolsComponent) : NavigationChild() {
|
||||
class ChecksumTools(private val component: ChecksumToolsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ChecksumToolsContent(component)
|
||||
}
|
||||
|
||||
class MeshGradients(val component: MeshGradientsComponent) : NavigationChild() {
|
||||
class MeshGradients(private val component: MeshGradientsComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = MeshGradientsContent(component)
|
||||
}
|
||||
|
||||
class EditExif(val component: EditExifComponent) : NavigationChild() {
|
||||
class EditExif(private val component: EditExifComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = EditExifContent(component)
|
||||
}
|
||||
|
||||
class ImageCutter(val component: ImageCutterComponent) : NavigationChild() {
|
||||
class ImageCutter(private val component: ImageCutterComponent) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = ImageCutterContent(component)
|
||||
}
|
||||
|
||||
class AudioCoverExtractor(
|
||||
private val component: AudioCoverExtractorComponent
|
||||
) : NavigationChild {
|
||||
@Composable
|
||||
override fun Content() = AudioCoverExtractorContent(component)
|
||||
}
|
||||
|
||||
}
|
@ -32,7 +32,6 @@ konfettiCompose = "2.0.5"
|
||||
shadowsPlus = "1.0.4"
|
||||
exifinterface = "1.4.0"
|
||||
firebaseAnalyticsKtx = "22.3.0"
|
||||
firebaseCrashlyticsGradle = "3.0.3"
|
||||
google-segmentationSelfie = "16.0.0-beta6"
|
||||
google-subjectSegmentation = "16.0.0-beta1"
|
||||
detekt = "1.23.8"
|
||||
@ -75,13 +74,18 @@ zxingAndroidEmbedded = "4.3.0"
|
||||
capturable = "3.0.1"
|
||||
moshi = "1.15.2"
|
||||
aboutlibraries = "12.0.0-a02+compose_1_8"
|
||||
aboutlibrariesGradle = "12.0.0-a04"
|
||||
junit = "4.13.2"
|
||||
bouncycastle = "1.80"
|
||||
evaluator = "1.0.0"
|
||||
ffmpeg-metadata-retriever = "1.0.19"
|
||||
|
||||
firebaseCrashlyticsGradle = "3.0.3"
|
||||
aboutlibrariesGradle = "12.0.0-a04"
|
||||
moduleGraphGradle = "0.12.0"
|
||||
|
||||
[libraries]
|
||||
ffmpeg-metadata-retriever-core = { module = "com.github.wseemann:FFmpegMediaMetadataRetriever-core", version.ref = "ffmpeg-metadata-retriever" }
|
||||
ffmpeg-metadata-retriever-native = { module = "com.github.wseemann:FFmpegMediaMetadataRetriever-native", version.ref = "ffmpeg-metadata-retriever" }
|
||||
evaluator = { module = "com.github.T8RIN:KotlinEvaluator", version.ref = "evaluator" }
|
||||
aboutlibraries-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlibraries" }
|
||||
moshi = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
|
||||
|
@ -109,6 +109,7 @@ include(":feature:checksum-tools")
|
||||
include(":feature:mesh-gradients")
|
||||
include(":feature:edit-exif")
|
||||
include(":feature:image-cutting")
|
||||
include(":feature:audio-cover-extractor")
|
||||
|
||||
include(":feature:root")
|
||||
|
||||
|
Reference in New Issue
Block a user