From 99f11fddf6e60fe8f0d0481a0872b099ac988cc4 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Tue, 24 Mar 2026 19:41:06 +0530 Subject: [PATCH 1/3] Add `${uuid}` special reference --- .../adityarajput/fileflow/data/models/Action.kt | 16 ++++++++++++++++ .../fileflow/services/FlowExecutor.kt | 8 +------- .../fileflow/viewmodels/UpsertRuleViewModel.kt | 14 +++++--------- .../fileflow/views/screens/OnboardingScreen.kt | 2 -- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/co/adityarajput/fileflow/data/models/Action.kt b/app/src/main/java/co/adityarajput/fileflow/data/models/Action.kt index d0786d2..bf0d9be 100644 --- a/app/src/main/java/co/adityarajput/fileflow/data/models/Action.kt +++ b/app/src/main/java/co/adityarajput/fileflow/data/models/Action.kt @@ -8,10 +8,13 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import co.adityarajput.fileflow.R import co.adityarajput.fileflow.data.Verb +import co.adityarajput.fileflow.utils.File import co.adityarajput.fileflow.utils.FileSuperlative import co.adityarajput.fileflow.utils.getGetDirectoryFromUri import co.adityarajput.fileflow.utils.toShortHumanReadableTime import kotlinx.serialization.Serializable +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid @Suppress("ClassName") @Serializable @@ -65,6 +68,19 @@ sealed class Action { withStyle(dullStyle) { append("\nas ") } append(destFileNameTemplate) } + + @OptIn(ExperimentalUuidApi::class) + fun getDestFileName(srcFile: File) = + srcFile.name!!.replace( + Regex(srcFileNamePattern), + destFileNameTemplate.replace( + $$"${uuid}", + Uuid.random().toString(), + ).replace( + $$"${folder}", + srcFile.parent?.name ?: "", + ), + ) } @Serializable diff --git a/app/src/main/java/co/adityarajput/fileflow/services/FlowExecutor.kt b/app/src/main/java/co/adityarajput/fileflow/services/FlowExecutor.kt index 4b388a8..2f26669 100644 --- a/app/src/main/java/co/adityarajput/fileflow/services/FlowExecutor.kt +++ b/app/src/main/java/co/adityarajput/fileflow/services/FlowExecutor.kt @@ -52,13 +52,7 @@ class FlowExecutor(private val context: Context) { continue } - val destFileName = srcFile.name!!.replace( - regex, - rule.action.destFileNameTemplate.replace( - $$"${folder}", - srcFile.parent?.name ?: "", - ), - ) + val destFileName = rule.action.getDestFileName(srcFile) var destFile = destSubDir.listChildren(false) .firstOrNull { it.isFile && it.name == destFileName } diff --git a/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt b/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt index 5cce07f..d3859e4 100644 --- a/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt +++ b/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt @@ -12,6 +12,7 @@ import co.adityarajput.fileflow.utils.File import co.adityarajput.fileflow.utils.FileSuperlative import co.adityarajput.fileflow.utils.Logger import co.adityarajput.fileflow.views.components.FolderPickerState +import kotlin.uuid.ExperimentalUuidApi class UpsertRuleViewModel( rule: Rule?, @@ -86,6 +87,7 @@ class UpsertRuleViewModel( var folderPickerState by mutableStateOf(null) + @OptIn(ExperimentalUuidApi::class) fun updateForm(context: Context, values: Values) { var currentSrcFiles: List? = null try { @@ -107,15 +109,9 @@ class UpsertRuleViewModel( ?.also { if (it.isEmpty()) warning = FormWarning.NO_MATCHES_IN_SRC } if (matchingSrcFiles != null && values.destFileNameTemplate.isNotBlank()) { - predictedDestFileNames = matchingSrcFiles.map { - it.name!!.replace( - regex, - values.destFileNameTemplate.replace( - $$"${folder}", - it.parent?.name ?: "", - ), - ) - }.distinct() + predictedDestFileNames = matchingSrcFiles + .map { (values.toRule().action as Action.MOVE).getDestFileName(it) } + .distinct() } } catch (_: Exception) { } diff --git a/app/src/main/java/co/adityarajput/fileflow/views/screens/OnboardingScreen.kt b/app/src/main/java/co/adityarajput/fileflow/views/screens/OnboardingScreen.kt index 5c2da60..73ce1fb 100644 --- a/app/src/main/java/co/adityarajput/fileflow/views/screens/OnboardingScreen.kt +++ b/app/src/main/java/co/adityarajput/fileflow/views/screens/OnboardingScreen.kt @@ -1,6 +1,5 @@ package co.adityarajput.fileflow.views.screens -import android.annotation.SuppressLint import android.os.Handler import android.os.Looper import androidx.compose.foundation.layout.* @@ -22,7 +21,6 @@ private val permissions = listOf( Permission.MANAGE_EXTERNAL_STORAGE, ) -@SuppressLint("BatteryLife") @Composable fun OnboardingScreen(goToRulesScreen: () -> Unit = {}) { val context = LocalContext.current From ddbf41682bee483d4c91955aca53fddef4268cb8 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Tue, 24 Mar 2026 20:02:50 +0530 Subject: [PATCH 2/3] Provide "don't rename" template shortcut in supporting text --- .../views/screens/UpsertRuleScreen.kt | 27 ++++++++++++++++++- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/co/adityarajput/fileflow/views/screens/UpsertRuleScreen.kt b/app/src/main/java/co/adityarajput/fileflow/views/screens/UpsertRuleScreen.kt index 676cb78..e58fd03 100644 --- a/app/src/main/java/co/adityarajput/fileflow/views/screens/UpsertRuleScreen.kt +++ b/app/src/main/java/co/adityarajput/fileflow/views/screens/UpsertRuleScreen.kt @@ -324,7 +324,31 @@ private fun ColumnScope.ActionPage(viewModel: UpsertRuleViewModel) { label = { Text(stringResource(R.string.file_name_template)) }, placeholder = { Text(stringResource(R.string.template_placeholder)) }, supportingText = { - if (viewModel.state.values.predictedDestFileNames?.isNotEmpty() ?: false) + if (viewModel.state.values.predictedDestFileNames.isNullOrEmpty()) { + Text( + buildAnnotatedString { + append(stringResource(R.string.blank_template_supporting)) + if (viewModel.state.values.destFileNameTemplate != "$0") { + withLink( + LinkAnnotation.Clickable( + "keep_name", + TextLinkStyles( + SpanStyle( + MaterialTheme.colorScheme.primary, + textDecoration = TextDecoration.Underline, + ), + ), + ) { + viewModel.updateForm( + context, + viewModel.state.values.copy(destFileNameTemplate = "$0"), + ) + }, + ) { append(stringResource(R.string.keep_name)) } + } + }, + ) + } else { Text( stringResource( R.string.template_will_yield, @@ -332,6 +356,7 @@ private fun ColumnScope.ActionPage(viewModel: UpsertRuleViewModel) { .joinToString(stringResource(R.string.or), limit = 3), ), ) + } }, colors = OutlinedTextFieldDefaults.colors( focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5944dfe..6fc05f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,6 +109,8 @@ Recreate source folder-structure in destination File name template Enter a regex template + "Template will be used to name the file in the destination. " + Keep original name? Template will yield %1$s Overwrite existing files in the destination, in case of conflict Mark stale if unmodified for From 9868753743d3a0aa8d7f3e3f8b5c38d1f062aec2 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Tue, 24 Mar 2026 20:05:37 +0530 Subject: [PATCH 3/3] Remove unnecessary `OptIn` --- .../co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt b/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt index d3859e4..2e232df 100644 --- a/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt +++ b/app/src/main/java/co/adityarajput/fileflow/viewmodels/UpsertRuleViewModel.kt @@ -12,7 +12,6 @@ import co.adityarajput.fileflow.utils.File import co.adityarajput.fileflow.utils.FileSuperlative import co.adityarajput.fileflow.utils.Logger import co.adityarajput.fileflow.views.components.FolderPickerState -import kotlin.uuid.ExperimentalUuidApi class UpsertRuleViewModel( rule: Rule?, @@ -87,7 +86,6 @@ class UpsertRuleViewModel( var folderPickerState by mutableStateOf(null) - @OptIn(ExperimentalUuidApi::class) fun updateForm(context: Context, values: Values) { var currentSrcFiles: List? = null try {