chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:31 +08:00
commit f06de36198
4585 changed files with 588418 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+85
View File
@@ -0,0 +1,85 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 = "com.t8rin.imagetoolbox.feature.root"
dependencies {
implementation(projects.feature.main)
implementation(projects.feature.loadNetImage)
implementation(projects.feature.crop)
implementation(projects.feature.limitsResize)
implementation(projects.feature.cipher)
implementation(projects.feature.imagePreview)
implementation(projects.feature.weightResize)
implementation(projects.feature.compare)
implementation(projects.feature.deleteExif)
implementation(projects.feature.paletteTools)
implementation(projects.feature.resizeConvert)
implementation(projects.feature.pdfTools)
implementation(projects.feature.singleEdit)
implementation(projects.feature.eraseBackground)
implementation(projects.feature.draw)
implementation(projects.feature.filters)
implementation(projects.feature.imageStitch)
implementation(projects.feature.pickColor)
implementation(projects.feature.recognizeText)
implementation(projects.feature.gradientMaker)
implementation(projects.feature.watermarking)
implementation(projects.feature.gifTools)
implementation(projects.feature.apngTools)
implementation(projects.feature.zip)
implementation(projects.feature.jxlTools)
implementation(projects.feature.settings)
implementation(projects.feature.easterEgg)
implementation(projects.feature.svgMaker)
implementation(projects.feature.formatConversion)
implementation(projects.feature.documentScanner)
implementation(projects.feature.scanQrCode)
implementation(projects.feature.imageStacking)
implementation(projects.feature.imageSplitting)
implementation(projects.feature.colorTools)
implementation(projects.feature.webpTools)
implementation(projects.feature.noiseGeneration)
implementation(projects.feature.textureGeneration)
implementation(projects.feature.collageMaker)
implementation(projects.feature.librariesInfo)
implementation(projects.feature.markupLayers)
implementation(projects.feature.base64Tools)
implementation(projects.feature.checksumTools)
implementation(projects.feature.meshGradients)
implementation(projects.feature.editExif)
implementation(projects.feature.imageCutting)
implementation(projects.feature.audioCoverExtractor)
implementation(projects.feature.libraryDetails)
implementation(projects.feature.wallpapersExport)
implementation(projects.feature.asciiArt)
implementation(projects.feature.aiTools)
implementation(projects.feature.colorLibrary)
implementation(projects.feature.appLogs)
implementation(projects.feature.shaderStudio)
implementation(projects.feature.help)
implementation(projects.feature.usageStatistics)
implementation(projects.feature.batchRename)
implementation(projects.feature.duplicateFinder)
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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>.
-->
<manifest>
</manifest>
@@ -0,0 +1,49 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.t8rin.imagetoolbox.core.ui.utils.provider.ImageToolboxCompositionLocals
import com.t8rin.imagetoolbox.feature.root.presentation.components.FileControllerEventsHandler
import com.t8rin.imagetoolbox.feature.root.presentation.components.RootDialogs
import com.t8rin.imagetoolbox.feature.root.presentation.components.ScreenSelector
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.uiSettingsState
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
@Composable
fun RootContent(
component: RootComponent
) {
val stack by component.childStack.subscribeAsState()
ImageToolboxCompositionLocals(
settingsState = component.uiSettingsState(),
filterPreviewModel = component.filterPreviewModel,
canSetDynamicFilterPreview = component.canSetDynamicFilterPreview,
currentScreen = stack.items.lastOrNull()?.configuration,
onNavigate = component::navigateTo
) {
ScreenSelector(component)
RootDialogs(component)
FileControllerEventsHandler(component)
}
}
@@ -0,0 +1,122 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components
import android.app.Activity
import android.content.IntentSender
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.t8rin.imagetoolbox.core.data.saving.FileControllerEvent
import com.t8rin.imagetoolbox.core.data.saving.FileControllerEventEmitter
import com.t8rin.imagetoolbox.core.resources.Icons
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Delete
import com.t8rin.imagetoolbox.core.ui.utils.helper.AppToastHost
import com.t8rin.imagetoolbox.core.utils.appContext
@Composable
internal fun FileControllerEventsHandler(
eventEmitter: FileControllerEventEmitter
) {
var activeRequestId by rememberSaveable { mutableStateOf<Long?>(null) }
var pendingRequests by rememberSaveable(stateSaver = deleteRequestsSaver) {
mutableStateOf(emptyList())
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) { result ->
activeRequestId?.let { requestId ->
eventEmitter.onDeleteOriginalsPermissionResult(
requestId = requestId,
granted = result.resultCode == Activity.RESULT_OK
)
}
activeRequestId = null
}
LaunchedEffect(eventEmitter) {
eventEmitter.events.collect { event ->
when (event) {
is FileControllerEvent.RequestDeleteOriginalsPermission -> {
pendingRequests = pendingRequests + event
}
is FileControllerEvent.OriginalFilesDeleteResult -> {
val message = when {
event.failed == 0 -> appContext.getString(
R.string.original_files_deleted,
event.deleted
)
event.deleted == 0 -> appContext.getString(
R.string.original_files_delete_failed,
event.failed
)
else -> appContext.getString(
R.string.original_files_delete_result,
event.deleted,
event.failed
)
}
AppToastHost.showToast(
message = message,
icon = Icons.Outlined.Delete
)
}
}
}
}
LaunchedEffect(activeRequestId, pendingRequests, launcher) {
if (activeRequestId == null) {
pendingRequests.firstOrNull()?.let { event ->
pendingRequests = pendingRequests.drop(1)
activeRequestId = event.requestId
launcher.launch(
IntentSenderRequest.Builder(event.intentSender).build()
)
}
}
}
}
private val deleteRequestsSaver = listSaver(
save = { requests ->
requests.flatMap { request ->
listOf(request.requestId, request.intentSender, request.count)
}
},
restore = { values ->
values.chunked(3).map { request ->
FileControllerEvent.RequestDeleteOriginalsPermission(
requestId = request[0] as Long,
intentSender = request[1] as IntentSender,
count = request[2] as Int
)
}
}
)
@@ -0,0 +1,96 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components
import androidx.compose.runtime.Composable
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalEditPresetsController
import com.t8rin.imagetoolbox.core.ui.utils.helper.Clipboard
import com.t8rin.imagetoolbox.core.ui.utils.helper.ReviewHandler
import com.t8rin.imagetoolbox.core.ui.widget.sheets.ProcessImagesPreferenceSheet
import com.t8rin.imagetoolbox.core.ui.widget.sheets.UpdateSheet
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.AppExitDialog
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.EditPresetsSheet
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.FirstLaunchSetupDialog
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.GithubReviewDialog
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.PermissionDialog
import com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs.TelegramGroupDialog
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.HandleLookForUpdates
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.SuccessRestoreBackupToastHandler
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
import com.t8rin.imagetoolbox.feature.settings.presentation.components.additional.DonateDialog
@Composable
internal fun RootDialogs(component: RootComponent) {
val editPresetsController = LocalEditPresetsController.current
AppExitDialog(component)
EditPresetsSheet(
visible = editPresetsController.isVisible,
onDismiss = editPresetsController::close,
onUpdatePresets = component::setPresets
)
ProcessImagesPreferenceSheet(
uris = component.uris ?: emptyList(),
extraDataType = component.extraDataType,
visible = component.showSelectDialog,
onDismiss = component::hideSelectDialog,
onNavigate = { screen ->
component.navigateTo(screen)
Clipboard.clear()
}
)
UpdateSheet(
tag = component.tag,
changelog = component.changelog,
visible = component.showUpdateDialog,
onDismiss = component::cancelledUpdate
)
FirstLaunchSetupDialog(
toggleShowUpdateDialog = component::toggleShowUpdateDialog,
toggleAllowBetas = component::toggleAllowBetas,
adjustPerformance = component::adjustPerformance
)
DonateDialog(
onRegisterDonateDialogOpen = component::registerDonateDialogOpen,
onNotShowDonateDialogAgain = component::notShowDonateDialogAgain
)
PermissionDialog()
GithubReviewDialog(
visible = component.showGithubReviewDialog,
onDismiss = component::hideReviewDialog,
onNotShowAgain = ReviewHandler.current::notShowReviewAgain,
isNotShowAgainButtonVisible = ReviewHandler.current.showNotShowAgainButton
)
TelegramGroupDialog(
visible = component.showTelegramGroupDialog,
onDismiss = component::hideTelegramGroupDialog,
onRedirected = component::registerTelegramGroupOpen
)
SuccessRestoreBackupToastHandler(component)
HandleLookForUpdates(component)
}
@@ -0,0 +1,64 @@
/*
* 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>.
*/
package com.t8rin.imagetoolbox.feature.root.presentation.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.t8rin.imagetoolbox.core.ui.utils.animation.toolboxPredictiveBackAnimation
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalCurrentScreen
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.ResetThemeOnGoBack
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.ScreenBasedMaxBrightnessEnforcement
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
@Composable
internal fun ScreenSelector(
component: RootComponent
) {
ResetThemeOnGoBack(component)
val childStack by component.childStack.subscribeAsState()
val currentScreen = LocalCurrentScreen.current
SettingsBackdropWrapper(
currentScreen = currentScreen,
concealBackdropFlow = component.concealBackdropFlow,
settingsComponent = component.settingsComponent,
children = {
Children(
stack = childStack,
modifier = Modifier.fillMaxSize(),
animation = remember(component) {
toolboxPredictiveBackAnimation(
backHandler = component.backHandler,
onBack = component::navigateBack
)
},
content = { child ->
child.instance.Content()
}
)
}
)
ScreenBasedMaxBrightnessEnforcement(currentScreen)
}
@@ -0,0 +1,208 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.material.BackdropScaffold
import androidx.compose.material.BackdropValue
import androidx.compose.material.Surface
import androidx.compose.material.rememberBackdropScaffoldState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import com.t8rin.gesture.detectPointerTransformGestures
import com.t8rin.imagetoolbox.core.settings.domain.model.FastSettingsSide
import com.t8rin.imagetoolbox.core.ui.utils.animation.FancyTransitionEasing
import com.t8rin.imagetoolbox.core.ui.utils.helper.PredictiveBackObserver
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalScreenSize
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedModalSheetDragHandle
import com.t8rin.imagetoolbox.feature.settings.presentation.SettingsContent
import com.t8rin.imagetoolbox.feature.settings.presentation.screenLogic.SettingsComponent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
@Composable
internal fun SettingsBackdropWrapper(
currentScreen: Screen?,
concealBackdropFlow: Flow<Boolean>,
settingsComponent: SettingsComponent,
children: @Composable () -> Unit
) {
val scaffoldState = rememberBackdropScaffoldState(
initialValue = BackdropValue.Concealed,
animationSpec = tween(
durationMillis = 400,
easing = FancyTransitionEasing
)
)
val canExpandSettings = ((currentScreen?.id ?: -1) >= 0)
.and(settingsComponent.settingsState.fastSettingsSide != FastSettingsSide.None)
var predictiveBackProgress by remember {
mutableFloatStateOf(0f)
}
val animatedPredictiveBackProgress by animateFloatAsState(predictiveBackProgress)
val clean = {
predictiveBackProgress = 0f
}
LaunchedEffect(canExpandSettings) {
if (!canExpandSettings) {
clean()
scaffoldState.conceal()
}
}
LaunchedEffect(concealBackdropFlow) {
concealBackdropFlow
.debounce(200)
.collectLatest {
if (it) {
clean()
scaffoldState.conceal()
}
}
}
val isTargetRevealed = scaffoldState.targetValue == BackdropValue.Revealed
val gridState = rememberLazyStaggeredGridState()
BackdropScaffold(
scaffoldState = scaffoldState,
appBar = {},
frontLayerContent = {
val alpha by animateFloatAsState(
if (isTargetRevealed) 1f else 0f
)
val color = MaterialTheme.colorScheme.surfaceContainerHigh.copy(alpha / 2f)
var isWantOpenSettings by remember {
mutableStateOf(false)
}
Box(
modifier = Modifier
.fillMaxSize()
.drawWithContent {
drawContent()
drawRect(color)
}
) {
Box(
modifier = Modifier.pointerInput(isWantOpenSettings) {
detectPointerTransformGestures(
consume = false,
onGestureEnd = {},
onGestureStart = {
isWantOpenSettings = false
},
onGesture = { _, _, _, _, _, _ -> }
)
},
content = {
children()
if (isTargetRevealed || scaffoldState.isRevealed) {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Transparent
) {}
}
}
)
SettingsOpenButton(
isWantOpenSettings = isWantOpenSettings,
onStateChange = { isWantOpenSettings = it },
scaffoldState = scaffoldState,
canExpandSettings = canExpandSettings
)
val progress = scaffoldState.progress(
from = BackdropValue.Revealed,
to = BackdropValue.Concealed
) * 20f
EnhancedModalSheetDragHandle(
color = Color.Transparent,
drawStroke = false,
bendAngle = (-15f * (1f - progress)).coerceAtMost(0f),
modifier = Modifier.alpha(alpha)
)
}
},
backLayerContent = {
if (canExpandSettings && (scaffoldState.isRevealed || isTargetRevealed)) {
PredictiveBackObserver(
onProgress = {
predictiveBackProgress = it * 1.3f
},
onClean = { isCompleted ->
if (isCompleted) scaffoldState.conceal()
clean()
},
enabled = isTargetRevealed
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(LocalScreenSize.current.height)
.alpha(1f - animatedPredictiveBackProgress)
) {
SettingsContent(
component = settingsComponent,
gridState = gridState
)
}
}
},
peekHeight = 0.dp,
headerHeight = 48.dp + WindowInsets.navigationBars.asPaddingValues()
.calculateBottomPadding(),
persistentAppBar = false,
frontLayerElevation = 0.dp,
backLayerBackgroundColor = MaterialTheme.colorScheme.surface,
frontLayerBackgroundColor = MaterialTheme.colorScheme.surface,
frontLayerScrimColor = Color.Transparent,
frontLayerShape = RectangleShape,
gesturesEnabled = scaffoldState.isRevealed
)
}
@@ -0,0 +1,247 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VisibilityThreshold
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.BackdropScaffoldState
import androidx.compose.material.Icon
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.t8rin.imagetoolbox.core.resources.icons.Settings
import com.t8rin.imagetoolbox.core.settings.domain.model.FastSettingsSide
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalSettingsState
import com.t8rin.imagetoolbox.core.ui.theme.blend
import com.t8rin.imagetoolbox.core.ui.theme.takeColorFromScheme
import com.t8rin.imagetoolbox.core.ui.utils.animation.springySpec
import com.t8rin.imagetoolbox.core.ui.utils.helper.rememberRipple
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.hapticsClickable
import com.t8rin.imagetoolbox.core.ui.widget.modifier.AutoCornersShape
import com.t8rin.imagetoolbox.core.ui.widget.modifier.animateShape
import com.t8rin.imagetoolbox.core.ui.widget.modifier.container
import kotlinx.coroutines.launch
@Composable
internal fun BoxScope.SettingsOpenButton(
isWantOpenSettings: Boolean,
onStateChange: (Boolean) -> Unit,
scaffoldState: BackdropScaffoldState,
canExpandSettings: Boolean
) {
val scope = rememberCoroutineScope()
val fastSettingsSide = LocalSettingsState.current.fastSettingsSide
val alignment = if (fastSettingsSide == FastSettingsSide.CenterStart) {
Alignment.CenterStart
} else {
Alignment.CenterEnd
}
val direction = LocalLayoutDirection.current
val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
val (startPadding, endPadding) = remember(paddingValues, direction) {
derivedStateOf {
paddingValues.calculateStartPadding(direction) to paddingValues.calculateEndPadding(
direction
)
}
}.value
val expandedPart = if (isWantOpenSettings) 12.dp else 42.dp
val cornerRadius = expandedPart.coerceAtLeast(4.dp)
val shape = animateShape(
targetValue = key(cornerRadius) {
if (fastSettingsSide == FastSettingsSide.CenterStart) {
if (startPadding == 0.dp) {
AutoCornersShape(
topEnd = cornerRadius,
bottomEnd = cornerRadius
)
} else {
AutoCornersShape(cornerRadius)
}
} else {
if (endPadding == 0.dp) {
AutoCornersShape(
topStart = cornerRadius,
bottomStart = cornerRadius
)
} else {
AutoCornersShape(cornerRadius)
}
}
},
animationSpec = spring(
dampingRatio = 0.5f,
stiffness = Spring.StiffnessLow
)
)
val height by animateDpAsState(
targetValue = if (isWantOpenSettings) 64.dp else 104.dp
)
val width by animateDpAsState(
targetValue = if (isWantOpenSettings) 48.dp else 24.dp,
animationSpec = springySpec()
)
val xOffset by animateDpAsState(
targetValue = if (!canExpandSettings) {
if (fastSettingsSide == FastSettingsSide.CenterStart) {
-width
} else {
width
}
} else {
0.dp
},
animationSpec = spring(
visibilityThreshold = Dp.VisibilityThreshold,
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
val alpha by animateFloatAsState(
targetValue = if (canExpandSettings) 1f else 0f,
animationSpec = tween(650)
)
val interactionSource = remember { MutableInteractionSource() }
Surface(
color = Color.Transparent,
modifier = Modifier
.align(alignment)
.padding(
start = startPadding,
end = endPadding
)
.size(
height = height,
width = width
)
.hapticsClickable(
enabled = canExpandSettings,
indication = null,
interactionSource = interactionSource
) {
if (isWantOpenSettings) {
scope.launch {
scaffoldState.reveal()
onStateChange(false)
}
} else {
onStateChange(true)
}
}
.alpha(alpha)
.offset {
IntOffset(
x = xOffset.roundToPx(),
y = 0
)
}
) {
Box {
val width by animateDpAsState(
targetValue = if (isWantOpenSettings) 48.dp else 4.dp,
animationSpec = spring(
dampingRatio = 0.35f,
stiffness = Spring.StiffnessLow
)
)
val containerColor = takeColorFromScheme {
tertiary.blend(primary, 0.65f)
}
val contentColor = takeColorFromScheme {
onTertiary.blend(onPrimary, 0.8f)
}
Box(
modifier = Modifier
.align(alignment)
.width(width)
.height(64.dp)
.container(
shape = shape,
resultPadding = 0.dp,
color = containerColor
)
.indication(
interactionSource = interactionSource,
indication = rememberRipple(contentColor = contentColor)
),
contentAlignment = Alignment.Center
) {
AnimatedVisibility(
visible = isWantOpenSettings,
enter = fadeIn() + scaleIn(
animationSpec = spring(
dampingRatio = 0.35f,
stiffness = Spring.StiffnessLow
)
),
exit = fadeOut() + scaleOut()
) {
Icon(
imageVector = Icons.Rounded.Settings,
contentDescription = null,
tint = contentColor
)
}
}
}
}
}
@@ -0,0 +1,102 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import android.os.Build
import androidx.activity.compose.BackHandler
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.DoorBack
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalComponentActivity
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalCurrentScreen
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
@Composable
internal fun AppExitDialog(component: RootComponent) {
val currentScreen = LocalCurrentScreen.current
if (currentScreen is Screen.Main) {
val context = LocalComponentActivity.current
var showExitDialog by rememberSaveable { mutableStateOf(false) }
val tiramisu = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
BackHandler(enabled = !tiramisu) {
if (component.shouldShowDialog) showExitDialog = true
else context.finishAffinity()
}
AppExitDialogImpl(
onDismiss = { showExitDialog = false },
visible = showExitDialog && !tiramisu
)
}
}
@Composable
private fun AppExitDialogImpl(
onDismiss: () -> Unit,
visible: Boolean
) {
val activity = LocalComponentActivity.current
EnhancedAlertDialog(
visible = visible,
onDismissRequest = onDismiss,
dismissButton = {
EnhancedButton(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
onClick = activity::finishAffinity
) {
Text(stringResource(R.string.close))
}
},
confirmButton = {
EnhancedButton(onClick = onDismiss) {
Text(stringResource(R.string.stay))
}
},
title = { Text(stringResource(R.string.app_closing)) },
text = {
Text(
stringResource(R.string.app_closing_sub),
textAlign = TextAlign.Center
)
},
icon = {
Icon(
imageVector = Icons.Outlined.DoorBack,
contentDescription = null
)
}
)
}
@@ -0,0 +1,204 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.text.isDigitsOnly
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.AddCircle
import com.t8rin.imagetoolbox.core.resources.icons.LabelPercent
import com.t8rin.imagetoolbox.core.resources.icons.PhotoSizeSelectSmall
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalSettingsState
import com.t8rin.imagetoolbox.core.ui.theme.outlineVariant
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedChip
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedModalBottomSheet
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.enhancedVerticalScroll
import com.t8rin.imagetoolbox.core.ui.widget.modifier.ShapeDefaults
import com.t8rin.imagetoolbox.core.ui.widget.text.AutoSizeText
import com.t8rin.imagetoolbox.core.ui.widget.text.TitleItem
@Composable
internal fun EditPresetsSheet(
visible: Boolean,
onDismiss: () -> Unit,
onUpdatePresets: (List<Int>) -> Unit
) {
val settingsState = LocalSettingsState.current
EnhancedModalBottomSheet(
visible = visible,
onDismiss = {
if (!it) onDismiss()
},
title = {
TitleItem(
text = stringResource(R.string.presets),
icon = Icons.Rounded.LabelPercent
)
},
sheetContent = {
val data = settingsState.presets
Box {
AnimatedContent(
targetState = data,
transitionSpec = { fadeIn() togetherWith fadeOut() },
modifier = Modifier
.fillMaxWidth()
.enhancedVerticalScroll(rememberScrollState())
) { list ->
var expanded by remember { mutableStateOf(false) }
FlowRow(
modifier = Modifier
.align(Alignment.Center)
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(
8.dp,
Alignment.CenterHorizontally
),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically)
) {
list.forEach {
EnhancedChip(
onClick = {
onUpdatePresets(list - it)
},
selected = false,
selectedColor = MaterialTheme.colorScheme.primary,
shape = MaterialTheme.shapes.medium
) {
AutoSizeText(it.toString())
}
}
EnhancedChip(
onClick = {
expanded = true
},
selected = false,
selectedColor = MaterialTheme.colorScheme.primary,
shape = MaterialTheme.shapes.medium,
label = {
Icon(
imageVector = Icons.Outlined.AddCircle,
contentDescription = stringResource(R.string.add)
)
}
)
}
var value by remember(expanded) { mutableStateOf("") }
EnhancedAlertDialog(
visible = expanded,
onDismissRequest = { expanded = false },
icon = {
Icon(
imageVector = Icons.Outlined.PhotoSizeSelectSmall,
contentDescription = null
)
},
title = {
Text(stringResource(R.string.presets))
},
text = {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
shape = ShapeDefaults.default,
value = value,
textStyle = MaterialTheme.typography.titleMedium.copy(
textAlign = TextAlign.Center
),
maxLines = 1,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
),
onValueChange = {
if (it.isDigitsOnly()) {
value = it.toIntOrNull()?.coerceAtMost(500)?.toString()
?: ""
}
},
placeholder = {
Text(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.enter_percent),
style = MaterialTheme.typography.titleMedium.copy(
textAlign = TextAlign.Center
),
color = LocalContentColor.current.copy(0.5f)
)
}
)
},
confirmButton = {
EnhancedButton(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
onClick = {
onUpdatePresets(
list + (value.toIntOrNull() ?: 0)
)
expanded = false
},
enabled = value.isNotEmpty()
) {
Text(stringResource(R.string.add))
}
}
)
}
}
},
confirmButton = {
EnhancedButton(
onClick = onDismiss,
borderColor = MaterialTheme.colorScheme.outlineVariant(),
containerColor = MaterialTheme.colorScheme.secondaryContainer,
) {
AutoSizeText(stringResource(R.string.close))
}
}
)
}
@@ -0,0 +1,150 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.t8rin.imagetoolbox.core.domain.model.PerformanceClass
import com.t8rin.imagetoolbox.core.domain.utils.Flavor
import com.t8rin.imagetoolbox.core.resources.Icons
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Beta
import com.t8rin.imagetoolbox.core.resources.icons.MobileArrowDown
import com.t8rin.imagetoolbox.core.resources.icons.ReleaseAlert
import com.t8rin.imagetoolbox.core.resources.icons.Webhook
import com.t8rin.imagetoolbox.core.settings.presentation.model.isFirstLaunch
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalSettingsState
import com.t8rin.imagetoolbox.core.ui.utils.helper.ContextUtils.isInstalledFromPlayStore
import com.t8rin.imagetoolbox.core.ui.utils.helper.ContextUtils.performanceClass
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.enhancedVerticalScroll
import com.t8rin.imagetoolbox.core.ui.widget.modifier.ShapeDefaults
import com.t8rin.imagetoolbox.core.ui.widget.modifier.fadingEdges
import com.t8rin.imagetoolbox.core.ui.widget.preferences.PreferenceItem
import com.t8rin.imagetoolbox.core.ui.widget.preferences.PreferenceRowSwitch
import com.t8rin.imagetoolbox.core.ui.widget.saver.OneTimeEffect
import com.t8rin.imagetoolbox.core.utils.appContext
@Composable
internal fun FirstLaunchSetupDialog(
toggleAllowBetas: () -> Unit,
toggleShowUpdateDialog: () -> Unit,
adjustPerformance: (PerformanceClass) -> Unit
) {
val settingsState = LocalSettingsState.current
var updateOnFirstOpen by rememberSaveable {
mutableStateOf(false)
}
OneTimeEffect {
updateOnFirstOpen = settingsState.isFirstLaunch(false)
adjustPerformance(appContext.performanceClass)
}
EnhancedAlertDialog(
visible = updateOnFirstOpen,
onDismissRequest = {},
icon = {
Icon(
imageVector = Icons.Rounded.MobileArrowDown,
contentDescription = null
)
},
title = {
Text(stringResource(R.string.updates))
},
text = {
val state = rememberScrollState()
ProvideTextStyle(value = LocalTextStyle.current.copy(textAlign = TextAlign.Left)) {
Column(
modifier = Modifier
.fadingEdges(
isVertical = true,
scrollableState = state,
scrollFactor = 1.1f
)
.enhancedVerticalScroll(state)
.padding(2.dp)
) {
if (Flavor.isFoss()) {
PreferenceItem(
title = stringResource(id = R.string.attention),
subtitle = stringResource(R.string.foss_update_checker_warning),
startIcon = Icons.Rounded.Webhook,
shape = ShapeDefaults.default,
modifier = Modifier.padding(bottom = 8.dp),
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
)
}
PreferenceRowSwitch(
shape = if (!appContext.isInstalledFromPlayStore()) {
ShapeDefaults.top
} else ShapeDefaults.default,
modifier = Modifier,
title = stringResource(R.string.check_updates),
subtitle = stringResource(R.string.check_updates_sub),
checked = settingsState.showUpdateDialogOnStartup,
onClick = {
toggleShowUpdateDialog()
},
startIcon = Icons.Rounded.ReleaseAlert
)
if (!appContext.isInstalledFromPlayStore()) {
Spacer(Modifier.height(4.dp))
PreferenceRowSwitch(
modifier = Modifier,
shape = ShapeDefaults.bottom,
title = stringResource(R.string.allow_betas),
subtitle = stringResource(R.string.allow_betas_sub),
checked = settingsState.allowBetas,
onClick = {
toggleAllowBetas()
},
startIcon = Icons.Rounded.Beta
)
}
}
}
},
confirmButton = {
EnhancedButton(
onClick = { updateOnFirstOpen = false }
) {
Text(stringResource(id = R.string.ok))
}
}
)
}
@@ -0,0 +1,78 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import com.t8rin.imagetoolbox.core.domain.APP_GITHUB_LINK
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Star
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
@Composable
internal fun GithubReviewDialog(
visible: Boolean,
onDismiss: () -> Unit,
isNotShowAgainButtonVisible: Boolean,
onNotShowAgain: () -> Unit
) {
val linkHandler = LocalUriHandler.current
EnhancedAlertDialog(
visible = visible,
onDismissRequest = onDismiss,
confirmButton = {
EnhancedButton(
onClick = {
linkHandler.openUri(APP_GITHUB_LINK)
}
) {
Text(text = stringResource(id = R.string.rate))
}
},
dismissButton = {
EnhancedButton(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
onClick = {
if (isNotShowAgainButtonVisible) onNotShowAgain()
onDismiss()
}
) {
Text(stringResource(id = R.string.close))
}
},
title = {
Text(stringResource(R.string.rate_app))
},
icon = {
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = null
)
},
text = {
Text(stringResource(R.string.rate_app_sub))
}
)
}
@@ -0,0 +1,116 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import android.Manifest
import android.os.Build
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.core.app.ActivityCompat
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Storage
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalSettingsState
import com.t8rin.imagetoolbox.core.ui.utils.helper.ContextUtils.needToShowStoragePermissionRequest
import com.t8rin.imagetoolbox.core.ui.utils.helper.ContextUtils.requestStoragePermission
import com.t8rin.imagetoolbox.core.ui.utils.permission.PermissionUtils.hasPermissionAllowed
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalComponentActivity
import com.t8rin.imagetoolbox.core.ui.utils.provider.rememberCurrentLifecycleEvent
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
import kotlinx.coroutines.delay
@Composable
internal fun PermissionDialog() {
val context = LocalComponentActivity.current
val settingsState = LocalSettingsState.current
var showDialog by remember { mutableStateOf(false) }
val currentLifecycleEvent = rememberCurrentLifecycleEvent()
LaunchedEffect(
showDialog,
context,
settingsState,
currentLifecycleEvent
) {
showDialog = context.needToShowStoragePermissionRequest() == true
while (showDialog) {
showDialog = context.needToShowStoragePermissionRequest() == true
delay(100)
}
}
var requestedOnce by rememberSaveable {
mutableStateOf(false)
}
LaunchedEffect(Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !requestedOnce) {
val notAllowed = listOf(
Manifest.permission.ACCESS_MEDIA_LOCATION,
Manifest.permission.POST_NOTIFICATIONS
).filter { !context.hasPermissionAllowed(it) }
if (notAllowed.isNotEmpty()) {
ActivityCompat.requestPermissions(
context,
buildList {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
add(Manifest.permission.READ_MEDIA_IMAGES)
}
addAll(notAllowed)
}.toTypedArray(),
0
)
}
requestedOnce = true
}
}
EnhancedAlertDialog(
visible = showDialog,
onDismissRequest = { },
icon = {
Icon(
imageVector = Icons.Rounded.Storage,
contentDescription = null
)
},
title = { Text(stringResource(R.string.permission)) },
text = {
Text(stringResource(R.string.permission_sub))
},
confirmButton = {
EnhancedButton(
onClick = {
context.requestStoragePermission()
}
) {
Text(stringResource(id = R.string.grant))
}
}
)
}
@@ -0,0 +1,92 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.dialogs
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import com.t8rin.imagetoolbox.core.domain.TELEGRAM_CHANNEL_LINK
import com.t8rin.imagetoolbox.core.domain.TELEGRAM_GROUP_LINK
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Cancel
import com.t8rin.imagetoolbox.core.resources.icons.Telegram
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedAlertDialog
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedButton
import com.t8rin.imagetoolbox.core.ui.widget.enhanced.EnhancedIconButton
@Composable
fun TelegramGroupDialog(
visible: Boolean,
onDismiss: () -> Unit,
onRedirected: () -> Unit
) {
val linkHandler = LocalUriHandler.current
EnhancedAlertDialog(
visible = visible,
icon = {
Icon(
imageVector = Icons.Rounded.Telegram,
contentDescription = "Telegram"
)
},
title = {
Text(stringResource(R.string.image_toolbox_in_telegram))
},
text = {
Text(stringResource(R.string.image_toolbox_in_telegram_sub))
},
confirmButton = {
EnhancedButton(
onClick = {
onRedirected()
linkHandler.openUri(TELEGRAM_GROUP_LINK)
}
) {
Text(stringResource(R.string.group))
}
},
dismissButton = {
EnhancedIconButton(
onClick = {
onRedirected()
},
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
) {
Icon(
imageVector = Icons.Rounded.Cancel,
contentDescription = null
)
}
EnhancedButton(
onClick = {
onRedirected()
linkHandler.openUri(TELEGRAM_CHANNEL_LINK)
},
containerColor = MaterialTheme.colorScheme.tertiaryContainer
) {
Text(stringResource(R.string.ci_channel))
}
},
onDismissRequest = onDismiss
)
}
@@ -0,0 +1,985 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.navigation
import com.arkivanov.decompose.ComponentContext
import com.t8rin.imagetoolbox.collage_maker.presentation.screenLogic.CollageMakerComponent
import com.t8rin.imagetoolbox.color_library.presentation.screenLogic.ColorLibraryComponent
import com.t8rin.imagetoolbox.color_tools.presentation.screenLogic.ColorToolsComponent
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.feature.ai_tools.presentation.screenLogic.AiToolsComponent
import com.t8rin.imagetoolbox.feature.apng_tools.presentation.screenLogic.ApngToolsComponent
import com.t8rin.imagetoolbox.feature.ascii_art.presentation.screenLogic.AsciiArtComponent
import com.t8rin.imagetoolbox.feature.audio_cover_extractor.ui.screenLogic.AudioCoverExtractorComponent
import com.t8rin.imagetoolbox.feature.base64_tools.presentation.screenLogic.Base64ToolsComponent
import com.t8rin.imagetoolbox.feature.batchrename.presentation.screenLogic.BatchRenameComponent
import com.t8rin.imagetoolbox.feature.checksum_tools.presentation.screenLogic.ChecksumToolsComponent
import com.t8rin.imagetoolbox.feature.cipher.presentation.screenLogic.CipherComponent
import com.t8rin.imagetoolbox.feature.compare.presentation.screenLogic.CompareComponent
import com.t8rin.imagetoolbox.feature.crop.presentation.screenLogic.CropComponent
import com.t8rin.imagetoolbox.feature.delete_exif.presentation.screenLogic.DeleteExifComponent
import com.t8rin.imagetoolbox.feature.document_scanner.presentation.screenLogic.DocumentScannerComponent
import com.t8rin.imagetoolbox.feature.draw.presentation.screenLogic.DrawComponent
import com.t8rin.imagetoolbox.feature.duplicate_finder.presentation.screenLogic.DuplicateFinderComponent
import com.t8rin.imagetoolbox.feature.easter_egg.presentation.screenLogic.EasterEggComponent
import com.t8rin.imagetoolbox.feature.edit_exif.presentation.screenLogic.EditExifComponent
import com.t8rin.imagetoolbox.feature.erase_background.presentation.screenLogic.EraseBackgroundComponent
import com.t8rin.imagetoolbox.feature.filters.presentation.screenLogic.FiltersComponent
import com.t8rin.imagetoolbox.feature.format_conversion.presentation.screenLogic.FormatConversionComponent
import com.t8rin.imagetoolbox.feature.gif_tools.presentation.screenLogic.GifToolsComponent
import com.t8rin.imagetoolbox.feature.gradient_maker.presentation.screenLogic.GradientMakerComponent
import com.t8rin.imagetoolbox.feature.help.presentation.screenLogic.HelpComponent
import com.t8rin.imagetoolbox.feature.image_preview.presentation.screenLogic.ImagePreviewComponent
import com.t8rin.imagetoolbox.feature.image_stacking.presentation.screenLogic.ImageStackingComponent
import com.t8rin.imagetoolbox.feature.image_stitch.presentation.screenLogic.ImageStitchingComponent
import com.t8rin.imagetoolbox.feature.jxl_tools.presentation.screenLogic.JxlToolsComponent
import com.t8rin.imagetoolbox.feature.libraries_info.presentation.screenLogic.LibrariesInfoComponent
import com.t8rin.imagetoolbox.feature.limits_resize.presentation.screenLogic.LimitsResizeComponent
import com.t8rin.imagetoolbox.feature.load_net_image.presentation.screenLogic.LoadNetImageComponent
import com.t8rin.imagetoolbox.feature.main.presentation.screenLogic.MainComponent
import com.t8rin.imagetoolbox.feature.markup_layers.presentation.screenLogic.MarkupLayersComponent
import com.t8rin.imagetoolbox.feature.mesh_gradients.presentation.screenLogic.MeshGradientsComponent
import com.t8rin.imagetoolbox.feature.palette_tools.presentation.screenLogic.PaletteToolsComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.compress.screenLogic.CompressPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.crop.screenLogic.CropPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_images.screenLogic.ExtractImagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_pages.screenLogic.ExtractPagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.flatten.screenLogic.FlattenPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.grayscale.screenLogic.GrayscalePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.images_to_pdf.screenLogic.ImagesToPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.merge.screenLogic.MergePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.metadata.screenLogic.MetadataPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.ocr.screenLogic.OCRPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.page_numbers.screenLogic.PageNumbersPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.preview.screenLogic.PreviewPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.print.screenLogic.PrintPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.protect.screenLogic.ProtectPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rearrange.screenLogic.RearrangePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_annotations.screenLogic.RemoveAnnotationsPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_pages.screenLogic.RemovePagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.repair.screenLogic.RepairPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.root.screenLogic.RootPdfToolsComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rotate.screenLogic.RotatePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.signature.screenLogic.SignaturePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.split.screenLogic.SplitPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.unlock.screenLogic.UnlockPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.watermark.screenLogic.WatermarkPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.zip_convert.screenLogic.ZipConvertPdfToolComponent
import com.t8rin.imagetoolbox.feature.pick_color.presentation.screenLogic.PickColorFromImageComponent
import com.t8rin.imagetoolbox.feature.recognize.text.presentation.screenLogic.RecognizeTextComponent
import com.t8rin.imagetoolbox.feature.resize_convert.presentation.screenLogic.ResizeAndConvertComponent
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.AiTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ApngTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.AppLogs
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.AsciiArt
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.AudioCoverExtractor
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Base64Tools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.BatchRename
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ChecksumTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Cipher
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.CollageMaker
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ColorLibrary
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ColorTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Compare
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.CompressPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Crop
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.CropPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.DeleteExif
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.DocumentScanner
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Draw
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.DuplicateFinder
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.EasterEgg
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.EditExif
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.EraseBackground
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ExtractImagesPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ExtractPagesPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Filter
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.FlattenPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.FormatConversion
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.GifTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.GradientMaker
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.GrayscalePdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Help
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImageCutter
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImagePreview
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImageSplitting
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImageStacking
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImageStitching
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ImagesToPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.JxlTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.LibrariesInfo
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.LibraryDetails
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.LimitResize
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.LoadNetImage
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Main
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.MarkupLayers
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.MergePdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.MeshGradients
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.MetadataPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.NoiseGeneration
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.OCRPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.PageNumbersPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.PaletteTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.PickColorFromImage
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.PreviewPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.PrintPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ProtectPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RearrangePdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RecognizeText
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RemoveAnnotationsPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RemovePagesPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RepairPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ResizeAndConvert
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RootPdfTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.RotatePdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ScanQrCode
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Settings
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ShaderStudio
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.SignaturePdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.SingleEdit
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.SplitPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.SvgMaker
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.TextureGeneration
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.UnlockPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.UsageStatistics
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.WallpapersExport
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.WatermarkPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Watermarking
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.WebpTools
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.WeightResize
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.Zip
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild.ZipConvertPdfTool
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
import com.t8rin.imagetoolbox.feature.scan_qr_code.presentation.screenLogic.ScanQrCodeComponent
import com.t8rin.imagetoolbox.feature.settings.presentation.screenLogic.SettingsComponent
import com.t8rin.imagetoolbox.feature.shader_studio.presentation.screenLogic.ShaderStudioComponent
import com.t8rin.imagetoolbox.feature.single_edit.presentation.screenLogic.SingleEditComponent
import com.t8rin.imagetoolbox.feature.svg_maker.presentation.screenLogic.SvgMakerComponent
import com.t8rin.imagetoolbox.feature.usage_statistics.presentation.screenLogic.UsageStatisticsComponent
import com.t8rin.imagetoolbox.feature.wallpapers_export.presentation.screenLogic.WallpapersExportComponent
import com.t8rin.imagetoolbox.feature.watermarking.presentation.screenLogic.WatermarkingComponent
import com.t8rin.imagetoolbox.feature.webp_tools.presentation.screenLogic.WebpToolsComponent
import com.t8rin.imagetoolbox.feature.weight_resize.presentation.screenLogic.WeightResizeComponent
import com.t8rin.imagetoolbox.feature.zip.presentation.screenLogic.ZipComponent
import com.t8rin.imagetoolbox.image_cutting.presentation.screenLogic.ImageCutterComponent
import com.t8rin.imagetoolbox.image_splitting.presentation.screenLogic.ImageSplitterComponent
import com.t8rin.imagetoolbox.library_details.presentation.screenLogic.LibraryDetailsComponent
import com.t8rin.imagetoolbox.noise_generation.presentation.screenLogic.NoiseGenerationComponent
import com.t8rin.imagetoolbox.presentation.app_logs.screenLogic.AppLogsComponent
import com.t8rin.imagetoolbox.texture_generation.presentation.screenLogic.TextureGenerationComponent
import javax.inject.Inject
internal class ChildProvider @Inject constructor(
private val apngToolsComponentFactory: ApngToolsComponent.Factory,
private val cipherComponentFactory: CipherComponent.Factory,
private val collageMakerComponentFactory: CollageMakerComponent.Factory,
private val compareComponentFactory: CompareComponent.Factory,
private val cropComponentFactory: CropComponent.Factory,
private val deleteExifComponentFactory: DeleteExifComponent.Factory,
private val documentScannerComponentFactory: DocumentScannerComponent.Factory,
private val drawComponentFactory: DrawComponent.Factory,
private val duplicateFinderComponentFactory: DuplicateFinderComponent.Factory,
private val eraseBackgroundComponentFactory: EraseBackgroundComponent.Factory,
private val filtersComponentFactory: FiltersComponent.Factory,
private val formatConversionComponentFactory: FormatConversionComponent.Factory,
private val paletteToolsComponentFactory: PaletteToolsComponent.Factory,
private val gifToolsComponentFactory: GifToolsComponent.Factory,
private val gradientMakerComponentFactory: GradientMakerComponent.Factory,
private val imagePreviewComponentFactory: ImagePreviewComponent.Factory,
private val imageSplittingComponentFactory: ImageSplitterComponent.Factory,
private val imageStackingComponentFactory: ImageStackingComponent.Factory,
private val imageStitchingComponentFactory: ImageStitchingComponent.Factory,
private val jxlToolsComponentFactory: JxlToolsComponent.Factory,
private val limitResizeComponentFactory: LimitsResizeComponent.Factory,
private val loadNetImageComponentFactory: LoadNetImageComponent.Factory,
private val noiseGenerationComponentFactory: NoiseGenerationComponent.Factory,
private val textureGenerationComponentFactory: TextureGenerationComponent.Factory,
private val rootPdfToolsComponentFactory: RootPdfToolsComponent.Factory,
private val pickColorFromImageComponentFactory: PickColorFromImageComponent.Factory,
private val recognizeTextComponentFactory: RecognizeTextComponent.Factory,
private val resizeAndConvertComponentFactory: ResizeAndConvertComponent.Factory,
private val scanQrCodeComponentFactory: ScanQrCodeComponent.Factory,
private val settingsComponentFactory: SettingsComponent.Factory,
private val shaderStudioComponentFactory: ShaderStudioComponent.Factory,
private val singleEditComponentFactory: SingleEditComponent.Factory,
private val svgMakerComponentFactory: SvgMakerComponent.Factory,
private val watermarkingComponentFactory: WatermarkingComponent.Factory,
private val webpToolsComponentFactory: WebpToolsComponent.Factory,
private val weightResizeComponentFactory: WeightResizeComponent.Factory,
private val zipComponentFactory: ZipComponent.Factory,
private val easterEggComponentFactory: EasterEggComponent.Factory,
private val colorToolsComponentFactory: ColorToolsComponent.Factory,
private val librariesInfoComponentFactory: LibrariesInfoComponent.Factory,
private val appLogsComponentFactory: AppLogsComponent.Factory,
private val usageStatisticsComponentFactory: UsageStatisticsComponent.Factory,
private val mainComponentFactory: MainComponent.Factory,
private val markupLayersComponentFactory: MarkupLayersComponent.Factory,
private val base64ToolsComponentFactory: Base64ToolsComponent.Factory,
private val checksumToolsComponentFactory: ChecksumToolsComponent.Factory,
private val meshGradientsComponentFactory: MeshGradientsComponent.Factory,
private val editExifComponentFactory: EditExifComponent.Factory,
private val imageCutterComponentFactory: ImageCutterComponent.Factory,
private val audioCoverExtractorComponentFactory: AudioCoverExtractorComponent.Factory,
private val libraryDetailsComponentFactory: LibraryDetailsComponent.Factory,
private val wallpapersExportComponentFactory: WallpapersExportComponent.Factory,
private val asciiArtComponentFactory: AsciiArtComponent.Factory,
private val aiToolsComponentFactory: AiToolsComponent.Factory,
private val colorLibraryComponentFactory: ColorLibraryComponent.Factory,
private val mergePdfToolComponentFactory: MergePdfToolComponent.Factory,
private val splitPdfToolComponentFactory: SplitPdfToolComponent.Factory,
private val rotatePdfToolComponentFactory: RotatePdfToolComponent.Factory,
private val rearrangePdfToolComponentFactory: RearrangePdfToolComponent.Factory,
private val pageNumbersPdfToolComponentFactory: PageNumbersPdfToolComponent.Factory,
private val ocrPdfToolComponentFactory: OCRPdfToolComponent.Factory,
private val watermarkPdfToolComponentFactory: WatermarkPdfToolComponent.Factory,
private val signaturePdfToolComponentFactory: SignaturePdfToolComponent.Factory,
private val protectPdfToolComponentFactory: ProtectPdfToolComponent.Factory,
private val unlockPdfToolComponentFactory: UnlockPdfToolComponent.Factory,
private val compressPdfToolComponentFactory: CompressPdfToolComponent.Factory,
private val grayscalePdfToolComponentFactory: GrayscalePdfToolComponent.Factory,
private val repairPdfToolComponentFactory: RepairPdfToolComponent.Factory,
private val metadataPdfToolComponentFactory: MetadataPdfToolComponent.Factory,
private val removePagesPdfToolComponentFactory: RemovePagesPdfToolComponent.Factory,
private val cropPdfToolComponentFactory: CropPdfToolComponent.Factory,
private val flattenPdfToolComponentFactory: FlattenPdfToolComponent.Factory,
private val extractImagesPdfToolComponentFactory: ExtractImagesPdfToolComponent.Factory,
private val zipConvertPdfToolComponentFactory: ZipConvertPdfToolComponent.Factory,
private val printPdfToolComponentFactory: PrintPdfToolComponent.Factory,
private val previewPdfToolComponentFactory: PreviewPdfToolComponent.Factory,
private val imagesToPdfToolComponentFactory: ImagesToPdfToolComponent.Factory,
private val extractPagesPdfToolComponentFactory: ExtractPagesPdfToolComponent.Factory,
private val removeAnnotationsPdfToolComponentFactory: RemoveAnnotationsPdfToolComponent.Factory,
private val helpComponentFactory: HelpComponent.Factory,
private val batchRenameComponentFactory: BatchRenameComponent.Factory,
) {
fun RootComponent.createChild(
config: Screen,
componentContext: ComponentContext
): NavigationChild = when (config) {
Screen.ColorTools -> ColorTools(
colorToolsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack
)
)
Screen.EasterEgg -> EasterEgg(
easterEggComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack
)
)
Screen.Main -> Main(
mainComponentFactory(
componentContext = componentContext,
onTryGetUpdate = ::tryGetUpdate,
onGetClipList = ::updateUris,
onNavigate = ::navigateToNew,
isUpdateAvailable = isUpdateAvailable
)
)
is Screen.ApngTools -> ApngTools(
apngToolsComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Cipher -> Cipher(
cipherComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack
)
)
is Screen.CollageMaker -> CollageMaker(
collageMakerComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Compare -> Compare(
compareComponentFactory(
componentContext = componentContext,
initialComparableUris = config.uris
?.takeIf { it.size == 2 }
?.let { it[0] to it[1] },
onGoBack = ::navigateBack
)
)
is Screen.Crop -> Crop(
cropComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.DeleteExif -> DeleteExif(
deleteExifComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.DuplicateFinder -> DuplicateFinder(
duplicateFinderComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
Screen.DocumentScanner -> DocumentScanner(
documentScannerComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Draw -> Draw(
drawComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.EraseBackground -> EraseBackground(
eraseBackgroundComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Filter -> Filter(
filtersComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.FormatConversion -> FormatConversion(
formatConversionComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.PaletteTools -> PaletteTools(
paletteToolsComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack
)
)
is Screen.GifTools -> GifTools(
gifToolsComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.GradientMaker -> GradientMaker(
gradientMakerComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ImagePreview -> ImagePreview(
imagePreviewComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ImageSplitting -> ImageSplitting(
imageSplittingComponentFactory(
componentContext = componentContext,
initialUris = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ImageStacking -> ImageStacking(
imageStackingComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ImageStitching -> ImageStitching(
imageStitchingComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.JxlTools -> JxlTools(
jxlToolsComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.LimitResize -> LimitResize(
limitResizeComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.LoadNetImage -> LoadNetImage(
loadNetImageComponentFactory(
componentContext = componentContext,
initialUrl = config.url,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
Screen.NoiseGeneration -> NoiseGeneration(
noiseGenerationComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo,
)
)
Screen.TextureGeneration -> TextureGeneration(
textureGenerationComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo,
)
)
is Screen.PdfTools -> RootPdfTools(
rootPdfToolsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.PickColorFromImage -> PickColorFromImage(
pickColorFromImageComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack
)
)
is Screen.RecognizeText -> RecognizeText(
recognizeTextComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack
)
)
is Screen.ResizeAndConvert -> ResizeAndConvert(
resizeAndConvertComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ScanQrCode -> ScanQrCode(
scanQrCodeComponentFactory(
componentContext = componentContext,
initialQrCodeContent = config.qrCodeContent,
uriToAnalyze = config.uriToAnalyze,
onGoBack = ::navigateBack
)
)
is Screen.Settings -> Settings(
settingsComponentFactory(
componentContext = componentContext,
onTryGetUpdate = ::tryGetUpdate,
onNavigate = ::navigateToNew,
isUpdateAvailable = isUpdateAvailable,
onGoBack = ::navigateBack,
initialSearchQuery = config.searchQuery,
targetSetting = config.targetSetting
)
)
Screen.ShaderStudio -> ShaderStudio(
shaderStudioComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack
)
)
is Screen.Help -> Help(
helpComponentFactory(
componentContext = componentContext,
initialCategory = config.categoryName,
initialTipId = config.tipId,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.SingleEdit -> SingleEdit(
singleEditComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onNavigate = ::navigateTo,
onGoBack = ::navigateBack
)
)
is Screen.SvgMaker -> SvgMaker(
svgMakerComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack
)
)
is Screen.Watermarking -> Watermarking(
watermarkingComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.WebpTools -> WebpTools(
webpToolsComponentFactory(
componentContext = componentContext,
initialType = config.type,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.WeightResize -> WeightResize(
weightResizeComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Zip -> Zip(
zipComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack
)
)
Screen.LibrariesInfo -> LibrariesInfo(
librariesInfoComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
Screen.AppLogs -> AppLogs(
appLogsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack
)
)
Screen.UsageStatistics -> UsageStatistics(
usageStatisticsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.MarkupLayers -> MarkupLayers(
markupLayersComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.Base64Tools -> Base64Tools(
base64ToolsComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ChecksumTools -> ChecksumTools(
checksumToolsComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack
)
)
is Screen.MeshGradients -> MeshGradients(
meshGradientsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.EditExif -> EditExif(
editExifComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ImageCutter -> ImageCutter(
imageCutterComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.AudioCoverExtractor -> AudioCoverExtractor(
audioCoverExtractorComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.LibraryDetails -> LibraryDetails(
libraryDetailsComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
libraryName = config.name,
libraryDescription = config.htmlDescription,
libraryLink = config.link
)
)
is Screen.WallpapersExport -> WallpapersExport(
wallpapersExportComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.AsciiArt -> AsciiArt(
asciiArtComponentFactory(
componentContext = componentContext,
initialUri = config.uri,
onGoBack = ::navigateBack
)
)
is Screen.AiTools -> AiTools(
aiToolsComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack,
onNavigate = ::navigateTo
)
)
is Screen.ColorLibrary -> ColorLibrary(
colorLibraryComponentFactory(
componentContext = componentContext,
onGoBack = ::navigateBack
)
)
is Screen.PdfTools.Merge -> MergePdfTool(
mergePdfToolComponentFactory(
initialUris = config.uris,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Split -> SplitPdfTool(
splitPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Rotate -> RotatePdfTool(
rotatePdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Rearrange -> RearrangePdfTool(
rearrangePdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.PageNumbers -> PageNumbersPdfTool(
pageNumbersPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.OCR -> OCRPdfTool(
ocrPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Watermark -> WatermarkPdfTool(
watermarkPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Signature -> SignaturePdfTool(
signaturePdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Protect -> ProtectPdfTool(
protectPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Unlock -> UnlockPdfTool(
unlockPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Compress -> CompressPdfTool(
compressPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Grayscale -> GrayscalePdfTool(
grayscalePdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Repair -> RepairPdfTool(
repairPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Metadata -> MetadataPdfTool(
metadataPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.RemovePages -> RemovePagesPdfTool(
removePagesPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Crop -> CropPdfTool(
cropPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Flatten -> FlattenPdfTool(
flattenPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.ExtractImages -> ExtractImagesPdfTool(
extractImagesPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.ZipConvert -> ZipConvertPdfTool(
zipConvertPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Print -> PrintPdfTool(
printPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.Preview -> PreviewPdfTool(
previewPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.ImagesToPdf -> ImagesToPdfTool(
imagesToPdfToolComponentFactory(
initialUris = config.uris,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.ExtractPages -> ExtractPagesPdfTool(
extractPagesPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.PdfTools.RemoveAnnotations -> RemoveAnnotationsPdfTool(
removeAnnotationsPdfToolComponentFactory(
initialUri = config.uri,
componentContext = componentContext,
onGoBack = ::navigateBack,
onNavigate = ::replaceTo
)
)
is Screen.BatchRename -> BatchRename(
batchRenameComponentFactory(
componentContext = componentContext,
initialUris = config.uris,
onGoBack = ::navigateBack
)
)
}
}
@@ -0,0 +1,600 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.navigation
import androidx.compose.runtime.Composable
import com.t8rin.imagetoolbox.collage_maker.presentation.CollageMakerContent
import com.t8rin.imagetoolbox.collage_maker.presentation.screenLogic.CollageMakerComponent
import com.t8rin.imagetoolbox.color_library.presentation.ColorLibraryContent
import com.t8rin.imagetoolbox.color_library.presentation.screenLogic.ColorLibraryComponent
import com.t8rin.imagetoolbox.color_tools.presentation.ColorToolsContent
import com.t8rin.imagetoolbox.color_tools.presentation.screenLogic.ColorToolsComponent
import com.t8rin.imagetoolbox.feature.ai_tools.presentation.AiToolsContent
import com.t8rin.imagetoolbox.feature.ai_tools.presentation.screenLogic.AiToolsComponent
import com.t8rin.imagetoolbox.feature.apng_tools.presentation.ApngToolsContent
import com.t8rin.imagetoolbox.feature.apng_tools.presentation.screenLogic.ApngToolsComponent
import com.t8rin.imagetoolbox.feature.ascii_art.presentation.AsciiArtContent
import com.t8rin.imagetoolbox.feature.ascii_art.presentation.screenLogic.AsciiArtComponent
import com.t8rin.imagetoolbox.feature.audio_cover_extractor.ui.AudioCoverExtractorContent
import com.t8rin.imagetoolbox.feature.audio_cover_extractor.ui.screenLogic.AudioCoverExtractorComponent
import com.t8rin.imagetoolbox.feature.base64_tools.presentation.Base64ToolsContent
import com.t8rin.imagetoolbox.feature.base64_tools.presentation.screenLogic.Base64ToolsComponent
import com.t8rin.imagetoolbox.feature.batchrename.presentation.BatchRenameContent
import com.t8rin.imagetoolbox.feature.batchrename.presentation.screenLogic.BatchRenameComponent
import com.t8rin.imagetoolbox.feature.checksum_tools.presentation.ChecksumToolsContent
import com.t8rin.imagetoolbox.feature.checksum_tools.presentation.screenLogic.ChecksumToolsComponent
import com.t8rin.imagetoolbox.feature.cipher.presentation.CipherContent
import com.t8rin.imagetoolbox.feature.cipher.presentation.screenLogic.CipherComponent
import com.t8rin.imagetoolbox.feature.compare.presentation.CompareContent
import com.t8rin.imagetoolbox.feature.compare.presentation.screenLogic.CompareComponent
import com.t8rin.imagetoolbox.feature.crop.presentation.CropContent
import com.t8rin.imagetoolbox.feature.crop.presentation.screenLogic.CropComponent
import com.t8rin.imagetoolbox.feature.delete_exif.presentation.DeleteExifContent
import com.t8rin.imagetoolbox.feature.delete_exif.presentation.screenLogic.DeleteExifComponent
import com.t8rin.imagetoolbox.feature.document_scanner.presentation.DocumentScannerContent
import com.t8rin.imagetoolbox.feature.document_scanner.presentation.screenLogic.DocumentScannerComponent
import com.t8rin.imagetoolbox.feature.draw.presentation.DrawContent
import com.t8rin.imagetoolbox.feature.draw.presentation.screenLogic.DrawComponent
import com.t8rin.imagetoolbox.feature.duplicate_finder.presentation.DuplicateFinderContent
import com.t8rin.imagetoolbox.feature.duplicate_finder.presentation.screenLogic.DuplicateFinderComponent
import com.t8rin.imagetoolbox.feature.easter_egg.presentation.EasterEggContent
import com.t8rin.imagetoolbox.feature.easter_egg.presentation.screenLogic.EasterEggComponent
import com.t8rin.imagetoolbox.feature.edit_exif.presentation.EditExifContent
import com.t8rin.imagetoolbox.feature.edit_exif.presentation.screenLogic.EditExifComponent
import com.t8rin.imagetoolbox.feature.erase_background.presentation.EraseBackgroundContent
import com.t8rin.imagetoolbox.feature.erase_background.presentation.screenLogic.EraseBackgroundComponent
import com.t8rin.imagetoolbox.feature.filters.presentation.FiltersContent
import com.t8rin.imagetoolbox.feature.filters.presentation.screenLogic.FiltersComponent
import com.t8rin.imagetoolbox.feature.format_conversion.presentation.FormatConversionContent
import com.t8rin.imagetoolbox.feature.format_conversion.presentation.screenLogic.FormatConversionComponent
import com.t8rin.imagetoolbox.feature.gif_tools.presentation.GifToolsContent
import com.t8rin.imagetoolbox.feature.gif_tools.presentation.screenLogic.GifToolsComponent
import com.t8rin.imagetoolbox.feature.gradient_maker.presentation.GradientMakerContent
import com.t8rin.imagetoolbox.feature.gradient_maker.presentation.screenLogic.GradientMakerComponent
import com.t8rin.imagetoolbox.feature.help.presentation.HelpContent
import com.t8rin.imagetoolbox.feature.help.presentation.screenLogic.HelpComponent
import com.t8rin.imagetoolbox.feature.image_preview.presentation.ImagePreviewContent
import com.t8rin.imagetoolbox.feature.image_preview.presentation.screenLogic.ImagePreviewComponent
import com.t8rin.imagetoolbox.feature.image_stacking.presentation.ImageStackingContent
import com.t8rin.imagetoolbox.feature.image_stacking.presentation.screenLogic.ImageStackingComponent
import com.t8rin.imagetoolbox.feature.image_stitch.presentation.ImageStitchingContent
import com.t8rin.imagetoolbox.feature.image_stitch.presentation.screenLogic.ImageStitchingComponent
import com.t8rin.imagetoolbox.feature.jxl_tools.presentation.JxlToolsContent
import com.t8rin.imagetoolbox.feature.jxl_tools.presentation.screenLogic.JxlToolsComponent
import com.t8rin.imagetoolbox.feature.libraries_info.presentation.LibrariesInfoContent
import com.t8rin.imagetoolbox.feature.libraries_info.presentation.screenLogic.LibrariesInfoComponent
import com.t8rin.imagetoolbox.feature.limits_resize.presentation.LimitsResizeContent
import com.t8rin.imagetoolbox.feature.limits_resize.presentation.screenLogic.LimitsResizeComponent
import com.t8rin.imagetoolbox.feature.load_net_image.presentation.LoadNetImageContent
import com.t8rin.imagetoolbox.feature.load_net_image.presentation.screenLogic.LoadNetImageComponent
import com.t8rin.imagetoolbox.feature.main.presentation.MainContent
import com.t8rin.imagetoolbox.feature.main.presentation.screenLogic.MainComponent
import com.t8rin.imagetoolbox.feature.markup_layers.presentation.MarkupLayersContent
import com.t8rin.imagetoolbox.feature.markup_layers.presentation.screenLogic.MarkupLayersComponent
import com.t8rin.imagetoolbox.feature.mesh_gradients.presentation.MeshGradientsContent
import com.t8rin.imagetoolbox.feature.mesh_gradients.presentation.screenLogic.MeshGradientsComponent
import com.t8rin.imagetoolbox.feature.palette_tools.presentation.PaletteToolsContent
import com.t8rin.imagetoolbox.feature.palette_tools.presentation.screenLogic.PaletteToolsComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.compress.CompressPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.compress.screenLogic.CompressPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.crop.CropPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.crop.screenLogic.CropPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_images.ExtractImagesPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_images.screenLogic.ExtractImagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_pages.ExtractPagesPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.extract_pages.screenLogic.ExtractPagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.flatten.FlattenPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.flatten.screenLogic.FlattenPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.grayscale.GrayscalePdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.grayscale.screenLogic.GrayscalePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.images_to_pdf.ImagesToPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.images_to_pdf.screenLogic.ImagesToPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.merge.MergePdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.merge.screenLogic.MergePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.metadata.MetadataPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.metadata.screenLogic.MetadataPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.ocr.OCRPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.ocr.screenLogic.OCRPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.page_numbers.PageNumbersPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.page_numbers.screenLogic.PageNumbersPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.preview.PreviewPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.preview.screenLogic.PreviewPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.print.PrintPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.print.screenLogic.PrintPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.protect.ProtectPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.protect.screenLogic.ProtectPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rearrange.RearrangePdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rearrange.screenLogic.RearrangePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_annotations.RemoveAnnotationsPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_annotations.screenLogic.RemoveAnnotationsPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_pages.RemovePagesPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.remove_pages.screenLogic.RemovePagesPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.repair.RepairPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.repair.screenLogic.RepairPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.root.RootPdfToolsContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.root.screenLogic.RootPdfToolsComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rotate.RotatePdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.rotate.screenLogic.RotatePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.signature.SignaturePdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.signature.screenLogic.SignaturePdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.split.SplitPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.split.screenLogic.SplitPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.unlock.UnlockPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.unlock.screenLogic.UnlockPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.watermark.WatermarkPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.watermark.screenLogic.WatermarkPdfToolComponent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.zip_convert.ZipConvertPdfToolContent
import com.t8rin.imagetoolbox.feature.pdf_tools.presentation.zip_convert.screenLogic.ZipConvertPdfToolComponent
import com.t8rin.imagetoolbox.feature.pick_color.presentation.PickColorFromImageContent
import com.t8rin.imagetoolbox.feature.pick_color.presentation.screenLogic.PickColorFromImageComponent
import com.t8rin.imagetoolbox.feature.recognize.text.presentation.RecognizeTextContent
import com.t8rin.imagetoolbox.feature.recognize.text.presentation.screenLogic.RecognizeTextComponent
import com.t8rin.imagetoolbox.feature.resize_convert.presentation.ResizeAndConvertContent
import com.t8rin.imagetoolbox.feature.resize_convert.presentation.screenLogic.ResizeAndConvertComponent
import com.t8rin.imagetoolbox.feature.scan_qr_code.presentation.ScanQrCodeContent
import com.t8rin.imagetoolbox.feature.scan_qr_code.presentation.screenLogic.ScanQrCodeComponent
import com.t8rin.imagetoolbox.feature.settings.presentation.SettingsContent
import com.t8rin.imagetoolbox.feature.settings.presentation.screenLogic.SettingsComponent
import com.t8rin.imagetoolbox.feature.shader_studio.presentation.ShaderStudioContent
import com.t8rin.imagetoolbox.feature.shader_studio.presentation.screenLogic.ShaderStudioComponent
import com.t8rin.imagetoolbox.feature.single_edit.presentation.SingleEditContent
import com.t8rin.imagetoolbox.feature.single_edit.presentation.screenLogic.SingleEditComponent
import com.t8rin.imagetoolbox.feature.svg_maker.presentation.SvgMakerContent
import com.t8rin.imagetoolbox.feature.svg_maker.presentation.screenLogic.SvgMakerComponent
import com.t8rin.imagetoolbox.feature.usage_statistics.presentation.UsageStatisticsContent
import com.t8rin.imagetoolbox.feature.usage_statistics.presentation.screenLogic.UsageStatisticsComponent
import com.t8rin.imagetoolbox.feature.wallpapers_export.presentation.WallpapersExportContent
import com.t8rin.imagetoolbox.feature.wallpapers_export.presentation.screenLogic.WallpapersExportComponent
import com.t8rin.imagetoolbox.feature.watermarking.presentation.WatermarkingContent
import com.t8rin.imagetoolbox.feature.watermarking.presentation.screenLogic.WatermarkingComponent
import com.t8rin.imagetoolbox.feature.webp_tools.presentation.WebpToolsContent
import com.t8rin.imagetoolbox.feature.webp_tools.presentation.screenLogic.WebpToolsComponent
import com.t8rin.imagetoolbox.feature.weight_resize.presentation.WeightResizeContent
import com.t8rin.imagetoolbox.feature.weight_resize.presentation.screenLogic.WeightResizeComponent
import com.t8rin.imagetoolbox.feature.zip.presentation.ZipContent
import com.t8rin.imagetoolbox.feature.zip.presentation.screenLogic.ZipComponent
import com.t8rin.imagetoolbox.image_cutting.presentation.ImageCutterContent
import com.t8rin.imagetoolbox.image_cutting.presentation.screenLogic.ImageCutterComponent
import com.t8rin.imagetoolbox.image_splitting.presentation.ImageSplitterContent
import com.t8rin.imagetoolbox.image_splitting.presentation.screenLogic.ImageSplitterComponent
import com.t8rin.imagetoolbox.library_details.presentation.LibraryDetailsContent
import com.t8rin.imagetoolbox.library_details.presentation.screenLogic.LibraryDetailsComponent
import com.t8rin.imagetoolbox.noise_generation.presentation.NoiseGenerationContent
import com.t8rin.imagetoolbox.noise_generation.presentation.screenLogic.NoiseGenerationComponent
import com.t8rin.imagetoolbox.presentation.app_logs.AppLogsContent
import com.t8rin.imagetoolbox.presentation.app_logs.screenLogic.AppLogsComponent
import com.t8rin.imagetoolbox.texture_generation.presentation.TextureGenerationContent
import com.t8rin.imagetoolbox.texture_generation.presentation.screenLogic.TextureGenerationComponent
internal sealed interface NavigationChild {
@Composable
fun Content()
class ApngTools(private val component: ApngToolsComponent) : NavigationChild {
@Composable
override fun Content() = ApngToolsContent(component)
}
class Cipher(private val component: CipherComponent) : NavigationChild {
@Composable
override fun Content() = CipherContent(component)
}
class CollageMaker(private val component: CollageMakerComponent) : NavigationChild {
@Composable
override fun Content() = CollageMakerContent(component)
}
class ColorTools(private val component: ColorToolsComponent) : NavigationChild {
@Composable
override fun Content() = ColorToolsContent(component)
}
class Compare(private val component: CompareComponent) : NavigationChild {
@Composable
override fun Content() = CompareContent(component)
}
class Crop(private val component: CropComponent) : NavigationChild {
@Composable
override fun Content() = CropContent(component)
}
class DeleteExif(private val component: DeleteExifComponent) : NavigationChild {
@Composable
override fun Content() = DeleteExifContent(component)
}
class BatchRename(private val component: BatchRenameComponent) : NavigationChild {
@Composable
override fun Content() = BatchRenameContent(component)
}
class DocumentScanner(private val component: DocumentScannerComponent) : NavigationChild {
@Composable
override fun Content() = DocumentScannerContent(component)
}
class Draw(private val component: DrawComponent) : NavigationChild {
@Composable
override fun Content() = DrawContent(component)
}
class DuplicateFinder(private val component: DuplicateFinderComponent) : NavigationChild {
@Composable
override fun Content() = DuplicateFinderContent(component)
}
class EasterEgg(private val component: EasterEggComponent) : NavigationChild {
@Composable
override fun Content() = EasterEggContent(component)
}
class EraseBackground(private val component: EraseBackgroundComponent) : NavigationChild {
@Composable
override fun Content() = EraseBackgroundContent(component)
}
class Filter(private val component: FiltersComponent) : NavigationChild {
@Composable
override fun Content() = FiltersContent(component)
}
class FormatConversion(private val component: FormatConversionComponent) : NavigationChild {
@Composable
override fun Content() = FormatConversionContent(component)
}
class PaletteTools(private val component: PaletteToolsComponent) : NavigationChild {
@Composable
override fun Content() = PaletteToolsContent(component)
}
class GifTools(private val component: GifToolsComponent) : NavigationChild {
@Composable
override fun Content() = GifToolsContent(component)
}
class GradientMaker(private val component: GradientMakerComponent) : NavigationChild {
@Composable
override fun Content() = GradientMakerContent(component)
}
class ImagePreview(private val component: ImagePreviewComponent) : NavigationChild {
@Composable
override fun Content() = ImagePreviewContent(component)
}
class ImageSplitting(private val component: ImageSplitterComponent) : NavigationChild {
@Composable
override fun Content() = ImageSplitterContent(component)
}
class ImageStacking(private val component: ImageStackingComponent) : NavigationChild {
@Composable
override fun Content() = ImageStackingContent(component)
}
class ImageStitching(private val component: ImageStitchingComponent) : NavigationChild {
@Composable
override fun Content() = ImageStitchingContent(component)
}
class JxlTools(private val component: JxlToolsComponent) : NavigationChild {
@Composable
override fun Content() = JxlToolsContent(component)
}
class LimitResize(private val component: LimitsResizeComponent) : NavigationChild {
@Composable
override fun Content() = LimitsResizeContent(component)
}
class LoadNetImage(private val component: LoadNetImageComponent) : NavigationChild {
@Composable
override fun Content() = LoadNetImageContent(component)
}
class Main(private val component: MainComponent) : NavigationChild {
@Composable
override fun Content() = MainContent(component)
}
class NoiseGeneration(private val component: NoiseGenerationComponent) : NavigationChild {
@Composable
override fun Content() = NoiseGenerationContent(component)
}
class TextureGeneration(private val component: TextureGenerationComponent) : NavigationChild {
@Composable
override fun Content() = TextureGenerationContent(component)
}
class RootPdfTools(private val component: RootPdfToolsComponent) : NavigationChild {
@Composable
override fun Content() = RootPdfToolsContent(component)
}
class PickColorFromImage(private val component: PickColorFromImageComponent) : NavigationChild {
@Composable
override fun Content() = PickColorFromImageContent(component)
}
class RecognizeText(private val component: RecognizeTextComponent) : NavigationChild {
@Composable
override fun Content() = RecognizeTextContent(component)
}
class ResizeAndConvert(private val component: ResizeAndConvertComponent) : NavigationChild {
@Composable
override fun Content() = ResizeAndConvertContent(component)
}
class ScanQrCode(private val component: ScanQrCodeComponent) : NavigationChild {
@Composable
override fun Content() = ScanQrCodeContent(component)
}
class Settings(private val component: SettingsComponent) : NavigationChild {
@Composable
override fun Content() = SettingsContent(component)
}
class ShaderStudio(private val component: ShaderStudioComponent) : NavigationChild {
@Composable
override fun Content() = ShaderStudioContent(component)
}
class Help(private val component: HelpComponent) : NavigationChild {
@Composable
override fun Content() = HelpContent(component)
}
class SingleEdit(private val component: SingleEditComponent) : NavigationChild {
@Composable
override fun Content() = SingleEditContent(component)
}
class SvgMaker(private val component: SvgMakerComponent) : NavigationChild {
@Composable
override fun Content() = SvgMakerContent(component)
}
class Watermarking(private val component: WatermarkingComponent) : NavigationChild {
@Composable
override fun Content() = WatermarkingContent(component)
}
class WebpTools(private val component: WebpToolsComponent) : NavigationChild {
@Composable
override fun Content() = WebpToolsContent(component)
}
class WeightResize(private val component: WeightResizeComponent) : NavigationChild {
@Composable
override fun Content() = WeightResizeContent(component)
}
class Zip(private val component: ZipComponent) : NavigationChild {
@Composable
override fun Content() = ZipContent(component)
}
class LibrariesInfo(private val component: LibrariesInfoComponent) : NavigationChild {
@Composable
override fun Content() = LibrariesInfoContent(component)
}
class AppLogs(private val component: AppLogsComponent) : NavigationChild {
@Composable
override fun Content() = AppLogsContent(component)
}
class UsageStatistics(private val component: UsageStatisticsComponent) : NavigationChild {
@Composable
override fun Content() = UsageStatisticsContent(component)
}
class MarkupLayers(private val component: MarkupLayersComponent) : NavigationChild {
@Composable
override fun Content() = MarkupLayersContent(component)
}
class Base64Tools(private val component: Base64ToolsComponent) : NavigationChild {
@Composable
override fun Content() = Base64ToolsContent(component)
}
class ChecksumTools(private val component: ChecksumToolsComponent) : NavigationChild {
@Composable
override fun Content() = ChecksumToolsContent(component)
}
class MeshGradients(private val component: MeshGradientsComponent) : NavigationChild {
@Composable
override fun Content() = MeshGradientsContent(component)
}
class EditExif(private val component: EditExifComponent) : NavigationChild {
@Composable
override fun Content() = EditExifContent(component)
}
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)
}
class LibraryDetails(private val component: LibraryDetailsComponent) : NavigationChild {
@Composable
override fun Content() = LibraryDetailsContent(component)
}
class WallpapersExport(private val component: WallpapersExportComponent) : NavigationChild {
@Composable
override fun Content() = WallpapersExportContent(component)
}
class AsciiArt(private val component: AsciiArtComponent) : NavigationChild {
@Composable
override fun Content() = AsciiArtContent(component)
}
class AiTools(private val component: AiToolsComponent) : NavigationChild {
@Composable
override fun Content() = AiToolsContent(component)
}
class ColorLibrary(private val component: ColorLibraryComponent) : NavigationChild {
@Composable
override fun Content() = ColorLibraryContent(component)
}
class MergePdfTool(private val component: MergePdfToolComponent) : NavigationChild {
@Composable
override fun Content() = MergePdfToolContent(component)
}
class SplitPdfTool(private val component: SplitPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = SplitPdfToolContent(component)
}
class RotatePdfTool(private val component: RotatePdfToolComponent) : NavigationChild {
@Composable
override fun Content() = RotatePdfToolContent(component)
}
class RearrangePdfTool(private val component: RearrangePdfToolComponent) : NavigationChild {
@Composable
override fun Content() = RearrangePdfToolContent(component)
}
class PageNumbersPdfTool(private val component: PageNumbersPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = PageNumbersPdfToolContent(component)
}
class OCRPdfTool(private val component: OCRPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = OCRPdfToolContent(component)
}
class WatermarkPdfTool(private val component: WatermarkPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = WatermarkPdfToolContent(component)
}
class SignaturePdfTool(private val component: SignaturePdfToolComponent) : NavigationChild {
@Composable
override fun Content() = SignaturePdfToolContent(component)
}
class ProtectPdfTool(private val component: ProtectPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = ProtectPdfToolContent(component)
}
class UnlockPdfTool(private val component: UnlockPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = UnlockPdfToolContent(component)
}
class CompressPdfTool(private val component: CompressPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = CompressPdfToolContent(component)
}
class GrayscalePdfTool(private val component: GrayscalePdfToolComponent) : NavigationChild {
@Composable
override fun Content() = GrayscalePdfToolContent(component)
}
class RepairPdfTool(private val component: RepairPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = RepairPdfToolContent(component)
}
class MetadataPdfTool(private val component: MetadataPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = MetadataPdfToolContent(component)
}
class RemovePagesPdfTool(private val component: RemovePagesPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = RemovePagesPdfToolContent(component)
}
class CropPdfTool(private val component: CropPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = CropPdfToolContent(component)
}
class FlattenPdfTool(private val component: FlattenPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = FlattenPdfToolContent(component)
}
class ExtractImagesPdfTool(private val component: ExtractImagesPdfToolComponent) :
NavigationChild {
@Composable
override fun Content() = ExtractImagesPdfToolContent(component)
}
class ZipConvertPdfTool(private val component: ZipConvertPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = ZipConvertPdfToolContent(component)
}
class PrintPdfTool(private val component: PrintPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = PrintPdfToolContent(component)
}
class PreviewPdfTool(private val component: PreviewPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = PreviewPdfToolContent(component)
}
class ImagesToPdfTool(private val component: ImagesToPdfToolComponent) : NavigationChild {
@Composable
override fun Content() = ImagesToPdfToolContent(component)
}
class ExtractPagesPdfTool(private val component: ExtractPagesPdfToolComponent) :
NavigationChild {
@Composable
override fun Content() = ExtractPagesPdfToolContent(component)
}
class RemoveAnnotationsPdfTool(private val component: RemoveAnnotationsPdfToolComponent) :
NavigationChild {
@Composable
override fun Content() = RemoveAnnotationsPdfToolContent(component)
}
}
@@ -0,0 +1,24 @@
/*
* 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>.
*/
package com.t8rin.imagetoolbox.feature.root.presentation.components.utils
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
fun interface BackEventObserver {
fun onBack(closedScreen: Screen?)
}
@@ -0,0 +1,44 @@
/*
* 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>.
*/
package com.t8rin.imagetoolbox.feature.root.presentation.components.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import com.t8rin.dynamic.theme.LocalDynamicThemeState
import com.t8rin.imagetoolbox.core.settings.presentation.provider.rememberAppColorTuple
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
@Composable
internal fun ResetThemeOnGoBack(
component: RootComponent
) {
val appColorTuple = rememberAppColorTuple()
val themeState = LocalDynamicThemeState.current
DisposableEffect(component, themeState, appColorTuple) {
val observer = BackEventObserver {
themeState.updateColorTuple(appColorTuple)
}
component.addBackEventsObserver(observer)
onDispose {
component.removeBackEventsObserver(observer)
}
}
}
@@ -0,0 +1,54 @@
/*
* 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>.
*/
package com.t8rin.imagetoolbox.feature.root.presentation.components.utils
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.util.fastAny
import com.t8rin.imagetoolbox.core.settings.presentation.provider.LocalSettingsState
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalComponentActivity
@Composable
internal fun ScreenBasedMaxBrightnessEnforcement(
currentScreen: Screen?
) {
val context = LocalComponentActivity.current
val listToForceBrightness = LocalSettingsState.current.screenListWithMaxBrightnessEnforcement
DisposableEffect(currentScreen) {
if (listToForceBrightness.fastAny { it == currentScreen?.id }) {
context.window.apply {
attributes.apply {
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
}
addFlags(WindowManager.LayoutParams.SCREEN_BRIGHTNESS_CHANGED)
}
}
onDispose {
context.window.apply {
attributes.apply {
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
}
addFlags(WindowManager.LayoutParams.SCREEN_BRIGHTNESS_CHANGED)
}
}
}
}
@@ -0,0 +1,68 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.value.Value
import com.t8rin.imagetoolbox.core.settings.domain.model.SettingsState
import com.t8rin.imagetoolbox.core.settings.presentation.model.UiSettingsState
import com.t8rin.imagetoolbox.core.settings.presentation.model.toUiState
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
import kotlinx.coroutines.delay
@Composable
internal fun RootComponent.uiSettingsState(): UiSettingsState = settingsState.toUiState(
randomEmojiKey = childStack.randomEmojiKey()
)
@Composable
internal fun HandleLookForUpdates(component: RootComponent) {
if (component.settingsState.shouldTryGetUpdate) {
LaunchedEffect(Unit) {
delay(500)
component.tryGetUpdate(
onNoUpdates = null
)
}
}
}
private val SettingsState.shouldTryGetUpdate: Boolean
get() = appOpenCount >= 2 && showUpdateDialogOnStartup
@Composable
private fun Value<ChildStack<Screen, *>>.randomEmojiKey(): Any {
val randomEmojiKey = remember {
mutableIntStateOf(0)
}
val currentDestination = subscribeAsState().value.items
LaunchedEffect(currentDestination) {
delay(200L) // Delay for transition
randomEmojiKey.intValue++
}
return randomEmojiKey.intValue
}
@@ -0,0 +1,51 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.components.utils
import com.t8rin.imagetoolbox.core.resources.Icons
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.Save
import com.t8rin.imagetoolbox.core.ui.utils.helper.AppToastHost
import com.t8rin.imagetoolbox.core.ui.utils.provider.LocalComponentActivity
import com.t8rin.imagetoolbox.feature.root.presentation.screenLogic.RootComponent
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@Composable
internal fun SuccessRestoreBackupToastHandler(component: RootComponent) {
val context = LocalComponentActivity.current
LaunchedEffect(component) {
component.backupRestoredEvents.collectLatest { restored ->
if (restored) {
launch {
AppToastHost.showConfetti()
//Wait for confetti to appear, then trigger font scale adjustment
delay(300L)
context.recreate()
}
AppToastHost.showToast(
message = context.getString(R.string.settings_restored),
icon = Icons.Rounded.Save
)
}
}
}
}
@@ -0,0 +1,498 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2026 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 com.t8rin.imagetoolbox.feature.root.presentation.screenLogic
import android.content.Intent
import android.net.Uri
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.childContext
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.items
import com.arkivanov.decompose.router.stack.navigate
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.arkivanov.decompose.value.MutableValue
import com.arkivanov.decompose.value.Value
import com.t8rin.imagetoolbox.core.data.saving.FileControllerEventEmitter
import com.t8rin.imagetoolbox.core.domain.APP_CHANGELOG
import com.t8rin.imagetoolbox.core.domain.coroutines.DispatchersHolder
import com.t8rin.imagetoolbox.core.domain.history.AppHistoryRepository
import com.t8rin.imagetoolbox.core.domain.model.ExtraDataType
import com.t8rin.imagetoolbox.core.domain.model.ImageModel
import com.t8rin.imagetoolbox.core.domain.model.PerformanceClass
import com.t8rin.imagetoolbox.core.domain.remote.AnalyticsManager
import com.t8rin.imagetoolbox.core.domain.resource.ResourceManager
import com.t8rin.imagetoolbox.core.domain.saving.FileController
import com.t8rin.imagetoolbox.core.domain.utils.smartJob
import com.t8rin.imagetoolbox.core.filters.domain.FilterParamsInteractor
import com.t8rin.imagetoolbox.core.resources.Icons
import com.t8rin.imagetoolbox.core.resources.R
import com.t8rin.imagetoolbox.core.resources.icons.AutoFixHigh
import com.t8rin.imagetoolbox.core.resources.icons.DownloadOff
import com.t8rin.imagetoolbox.core.resources.icons.Error
import com.t8rin.imagetoolbox.core.settings.domain.AutoCacheCleanupUseCase
import com.t8rin.imagetoolbox.core.settings.domain.SettingsManager
import com.t8rin.imagetoolbox.core.settings.domain.model.SettingsState
import com.t8rin.imagetoolbox.core.ui.utils.BaseComponent
import com.t8rin.imagetoolbox.core.ui.utils.helper.AppToastHost
import com.t8rin.imagetoolbox.core.ui.utils.helper.handleDeeplinks
import com.t8rin.imagetoolbox.core.ui.utils.navigation.Screen
import com.t8rin.imagetoolbox.core.ui.utils.state.update
import com.t8rin.imagetoolbox.core.ui.widget.other.ToastDuration
import com.t8rin.imagetoolbox.core.utils.isNeedUpdate
import com.t8rin.imagetoolbox.core.utils.makeLog
import com.t8rin.imagetoolbox.core.utils.parseChangelog
import com.t8rin.imagetoolbox.core.utils.toImageModel
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.ChildProvider
import com.t8rin.imagetoolbox.feature.root.presentation.components.navigation.NavigationChild
import com.t8rin.imagetoolbox.feature.root.presentation.components.utils.BackEventObserver
import com.t8rin.imagetoolbox.feature.settings.presentation.screenLogic.SettingsComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.javaio.toInputStream
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
class RootComponent @AssistedInject internal constructor(
@Assisted componentContext: ComponentContext,
private val settingsManager: SettingsManager,
private val childProvider: ChildProvider,
private val analyticsManager: AnalyticsManager,
private val client: HttpClient,
private val filterParamsInteractor: FilterParamsInteractor,
private val fileController: FileController,
private val appHistoryRepository: AppHistoryRepository,
private val autoCacheCleanupUseCase: AutoCacheCleanupUseCase,
dispatchersHolder: DispatchersHolder,
settingsComponentFactory: SettingsComponent.Factory,
resourceManager: ResourceManager,
fileControllerEventEmitter: FileControllerEventEmitter
) : BaseComponent(dispatchersHolder, componentContext),
ResourceManager by resourceManager,
FileControllerEventEmitter by fileControllerEventEmitter {
private var updatesJob: Job? by smartJob()
private val _backupRestoredEvents: Channel<Boolean> = Channel(Channel.BUFFERED)
val backupRestoredEvents: Flow<Boolean> = _backupRestoredEvents.receiveAsFlow()
private val _isUpdateAvailable: MutableValue<Boolean> = MutableValue(false)
val isUpdateAvailable: Value<Boolean> = _isUpdateAvailable
private val _concealBackdropChannel: Channel<Boolean> = Channel(Channel.BUFFERED)
val concealBackdropFlow: Flow<Boolean> = _concealBackdropChannel.receiveAsFlow()
val settingsComponent = settingsComponentFactory(
componentContext = childContext("rootSettings"),
onTryGetUpdate = ::tryGetUpdate,
onNavigate = ::navigateTo,
isUpdateAvailable = isUpdateAvailable,
onGoBack = {
_concealBackdropChannel.trySend(true)
},
initialSearchQuery = ""
)
private val _settingsState = mutableStateOf(SettingsState.Default)
val settingsState: SettingsState by _settingsState
private val navController = StackNavigation<Screen>()
internal val childStack: Value<ChildStack<Screen, NavigationChild>> by lazy {
childStack(
source = navController,
initialConfiguration = Screen.Main,
serializer = Screen.serializer(),
handleBackButton = true,
childFactory = { screen, context ->
with(childProvider) {
createChild(
config = screen,
componentContext = context
)
}
}
)
}
private val _uris = mutableStateOf<List<Uri>?>(null)
val uris by _uris
private val _extraDataType = mutableStateOf<ExtraDataType?>(null)
val extraDataType: ExtraDataType? by _extraDataType
private val _showSelectDialog = mutableStateOf(false)
val showSelectDialog by _showSelectDialog
private val _showUpdateDialog = mutableStateOf(false)
val showUpdateDialog by _showUpdateDialog
private val _isUpdateCancelled = mutableStateOf(false)
private val _shouldShowExitDialog = mutableStateOf(true)
val shouldShowDialog by _shouldShowExitDialog
private val _showGithubReviewDialog = mutableStateOf(false)
val showGithubReviewDialog by _showGithubReviewDialog
private val _showTelegramGroupDialog = mutableStateOf(false)
val showTelegramGroupDialog by _showTelegramGroupDialog
private val _tag = mutableStateOf("")
val tag by _tag
private val _changelog = mutableStateOf("")
val changelog by _changelog
private val _filterPreviewModel: MutableState<ImageModel> =
mutableStateOf(R.drawable.filter_preview_source.toImageModel())
val filterPreviewModel by _filterPreviewModel
private val _canSetDynamicFilterPreview: MutableState<Boolean> =
mutableStateOf(false)
val canSetDynamicFilterPreview by _canSetDynamicFilterPreview
init {
runBlocking {
_settingsState.value = settingsManager.getSettingsState().also {
autoCacheCleanupUseCase.clearCacheIfNeeded(it)
}
}
settingsManager
.settingsState
.onEach { state ->
_showTelegramGroupDialog.update {
state.appOpenCount % 6 == 0 && state.appOpenCount != 0 && !state.isTelegramGroupOpened
}
_settingsState.value = state
}
.launchIn(componentScope)
if (settingsState.screenList.size != Screen.entries.size) {
componentScope.launch {
val currentList = settingsState.screenList
val neededList = Screen.entries.filter { it.id !in currentList }.map { it.id }
settingsManager.setScreenOrder(
(currentList + neededList).joinToString("/")
)
}
}
filterParamsInteractor
.getFilterPreviewModel().onEach { data ->
_filterPreviewModel.update { data }
}.launchIn(componentScope)
filterParamsInteractor
.getCanSetDynamicFilterPreview().onEach { value ->
_canSetDynamicFilterPreview.update { value }
}.launchIn(componentScope)
}
fun toggleShowUpdateDialog() {
componentScope.launch {
settingsManager.toggleShowUpdateDialogOnStartup()
}
}
fun setPresets(newPresets: List<Int>) {
componentScope.launch {
settingsManager.setPresets(newPresets)
}
}
fun cancelledUpdate(showAgain: Boolean = false) {
if (!showAgain) _isUpdateCancelled.value = true
_showUpdateDialog.value = false
}
fun tryGetUpdate(
isNewRequest: Boolean = false,
onNoUpdates: (() -> Unit)? = {
AppToastHost.showToast(
icon = Icons.Rounded.DownloadOff,
message = getString(R.string.no_updates)
)
}
) {
if (settingsState.appOpenCount < 2 && !isNewRequest) return
val isInstalledFromMarket = settingsManager.isInstalledFromPlayStore()
val showDialog = settingsState.showUpdateDialogOnStartup
if (isInstalledFromMarket) {
if (showDialog) {
_showUpdateDialog.value = isNewRequest
}
} else {
if (!_isUpdateCancelled.value || isNewRequest) {
updatesJob = componentScope.launch {
checkForUpdates(
showDialog = showDialog,
onNoUpdates = onNoUpdates ?: {}
)
}
}
}
}
private suspend fun checkForUpdates(
showDialog: Boolean,
onNoUpdates: () -> Unit
) = withContext(defaultDispatcher) {
"start updates check".makeLog("checkForUpdates")
runCatching {
val (tag, changelog) = client
.get(APP_CHANGELOG).bodyAsChannel().toInputStream()
.use { it.parseChangelog() }
_tag.update { tag }
_changelog.update { changelog }
val isNeedUpdate = isNeedUpdate(
updateName = tag,
allowBetas = settingsState.allowBetas
).makeLog("checkForUpdates") { "isNeedUpdate = $it" }
if (isNeedUpdate) {
_isUpdateAvailable.value = true
_showUpdateDialog.value = showDialog
} else {
onNoUpdates()
}
}.onFailure {
it.makeLog("checkForUpdates")
onNoUpdates()
}
}
fun hideSelectDialog() {
_showSelectDialog.value = false
_uris.update { null }
}
fun updateUris(uris: List<Uri>) {
_uris.value = uris
if (uris.isNotEmpty() || extraDataType != null) {
_showSelectDialog.value = true
}
}
fun updateExtraDataType(type: ExtraDataType?) {
type.makeLog("updateExtraDataType")
if (type is ExtraDataType.Backup) {
componentScope.launch {
settingsManager.restoreFromBackupFile(
backupFileUri = type.uri.trim(),
onSuccess = {
_backupRestoredEvents.trySend(true)
},
onFailure = { throwable ->
AppToastHost.showToast(
message = getString(
R.string.smth_went_wrong,
throwable.localizedMessage ?: ""
),
icon = Icons.Outlined.Error,
duration = ToastDuration.Long
)
_backupRestoredEvents.trySend(false)
}
)
}
return
}
if (type is ExtraDataType.Template) {
componentScope.launch {
val content = fileController.readBytes(type.uri).toString(Charsets.UTF_8)
if (filterParamsInteractor.isValidTemplateFilter(content)) {
filterParamsInteractor.addTemplateFilterFromString(
string = content,
onSuccess = { filterName, filtersCount ->
AppToastHost.showToast(
message = getString(
R.string.added_filter_template,
filterName,
filtersCount
),
icon = Icons.Outlined.AutoFixHigh
)
},
onFailure = {}
)
}
}
return
}
_extraDataType.update { null }
_extraDataType.update { type }
}
fun cancelShowingExitDialog() {
_shouldShowExitDialog.update { false }
}
fun toggleAllowBetas() {
componentScope.launch {
settingsManager.toggleAllowBetas()
}
}
fun onWantGithubReview() {
_showGithubReviewDialog.update { true }
}
fun hideReviewDialog() {
_showGithubReviewDialog.update { false }
}
fun hideTelegramGroupDialog() {
_showTelegramGroupDialog.update { false }
}
fun adjustPerformance(performanceClass: PerformanceClass) {
componentScope.launch {
settingsManager.adjustPerformance(performanceClass)
}
}
fun registerDonateDialogOpen() {
componentScope.launch {
settingsManager.registerDonateDialogOpen()
}
}
fun notShowDonateDialogAgain() {
componentScope.launch {
settingsManager.setNotShowDonateDialogAgain()
}
}
fun registerTelegramGroupOpen() {
componentScope.launch {
settingsManager.registerTelegramGroupOpen()
}
}
fun navigateTo(screen: Screen) {
componentScope.launch {
delay(100)
screen.saveToHistory().simpleName.makeLog("Navigator")
.also(analyticsManager::registerScreenOpen)
navController.pushNew(screen)
hideSelectDialog()
}
}
fun replaceTo(screen: Screen) {
componentScope.launch {
delay(100)
screen.saveToHistory().simpleName.makeLog("Navigator")
.also(analyticsManager::registerScreenOpen)
navController.navigate(
transformer = { stack ->
stack.dropLastWhile { it !is Screen.PdfTools && it !is Screen.Main } + screen
}
)
hideSelectDialog()
}
}
fun navigateToNew(screen: Screen) {
if (childStack.items.lastOrNull()?.configuration != Screen.Main) {
navigateBack()
}
screen.saveToHistory().simpleName.makeLog("Navigator")
.also(analyticsManager::registerScreenOpen)
navController.pushNew(screen)
}
private val backEventsObservers: MutableList<BackEventObserver> = mutableListOf()
fun navigateBack() {
val closedScreen = childStack.items.lastOrNull()?.configuration
backEventsObservers.forEach { observer ->
observer.onBack(closedScreen)
}
hideSelectDialog()
"Pop ${closedScreen?.simpleName}".makeLog("Navigator")
navController.pop()
}
fun addBackEventsObserver(
observer: BackEventObserver
) {
backEventsObservers.add(observer)
}
fun removeBackEventsObserver(
observer: BackEventObserver
) {
backEventsObservers.remove(observer)
}
fun handleDeeplinks(intent: Intent?) {
intent.handleDeeplinks(
onStart = ::hideSelectDialog,
onHasExtraDataType = ::updateExtraDataType,
onColdStart = ::cancelShowingExitDialog,
onGetUris = ::updateUris,
onNavigate = ::navigateTo,
isHasUris = !uris.isNullOrEmpty(),
onWantGithubReview = ::onWantGithubReview,
isOpenEditInsteadOfPreview = settingsState.openEditInsteadOfPreview
)
}
private fun Screen.saveToHistory() = apply {
if (id < 0) return@apply
componentScope.launch {
appHistoryRepository.pushLastTool(id)
}
}
@AssistedFactory
fun interface Factory {
operator fun invoke(
componentContext: ComponentContext
): RootComponent
}
}