diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index a4a1b55..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: BURG3R5 - ---- - -**Describe the bug** - - -**To Reproduce** - - -**Expected behavior** - - -**Screenshots** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..3d8aaf6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,37 @@ +name: Bug report +description: "Report unexpected behavior or crashes" + +labels: bug +assignees: BURG3R5 + +body: + - type: textarea + attributes: + label: Problem description + description: "A clear and concise description of the problem." + validations: + required: true + - type: textarea + attributes: + label: Reproduction steps + description: "Steps to reproduce the bug." + validations: + required: true + - type: textarea + attributes: + label: Expected behavior + description: "A clear and concise description of what you expected to happen." + validations: + required: true + - type: textarea + attributes: + label: Screenshots and logs + description: "Any relevant screenshots and logs." + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: "Any other context about the bug report." + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index d370939..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: BURG3R5 - ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the solution you'd like** - - -**Describe alternatives you've considered** - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000..dc3cf8e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,32 @@ +name: Feature request +description: "Suggest a new feature or an improvement to an existing one" + +labels: enhancement +assignees: BURG3R5 + +body: + - type: textarea + attributes: + label: Problem description + description: "A clear and concise description of the problem." + placeholder: "I'm always frustrated when..." + validations: + required: true + - type: textarea + attributes: + label: Proposed solution + description: "The solution you'd like to see implemented." + validations: + required: true + - type: textarea + attributes: + label: Alternatives considered + description: "Any alternative solutions or features you've considered." + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: "Any other context, examples, or screenshots about the feature request." + validations: + required: false diff --git a/.github/contributing.md b/.github/contributing.md new file mode 100644 index 0000000..e60607b --- /dev/null +++ b/.github/contributing.md @@ -0,0 +1,49 @@ +# Contribution Guidelines + +Thank you for your interest in contributing to this project. And for taking the time to read this +document! All forms of contributions are welcome, including issues, pulls, designs, and +translations. Please read the following guidelines to ensure minimal friction in the process. + +### "AI" Policy + +Contributions containing significant generated content will not be accepted. The corporations behind +these tools have demonstrated disregard for the environment, the rights of consumers, as well as +real people displaced/affected by their data-centers. + +From a more technical point-of-view, this project is not some generic CRUD+API app full of basic +boilerplate. Understanding the codebase and thorough testing is necessary to ensure the project +works in all cases and can be maintained in the future. + +Bot-accounts that do not declare themselves as such will be reported and blocked. + +## Issues + +Bug reports, feature requests and other issues can be created using the relevant templates in the " +Issues" tab on GitHub. If you don't wish to use a GitHub account, you can also contact me via email +at `mail@adityarajput.co`. + +## Pulls + +If you wish to help out with an existing bug report or feature request, leave a comment on that +issue saying so. If there is no existing issue, open a new one and mention that you would like to +work on it. + +Please ensure atomic commits and focused PRs. + +## Designs + +Design contributions will be treated just like code contributions. Please ensure that your designs +do not infringe on any existing IP, and that you have the necessary rights to use any assets +included in your design. + +## Translations + +This project is currently only available in English. If you would like to translate, please open an +issue so we can discuss the workflow. + +## Shares + +This project was originally created for personal use, and will remain FLOSS and up-to-date as long +as I'm around. There will never be any links asking for monetary contributions. However, as most +brains do, mine likes to see numbers go up, and so if you like the project, please do share it with +people who might find it useful and/or leave a star on GitHub. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7184ac6..022fe27 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + alias(libs.plugins.aboutlibraries.android) alias(libs.plugins.android.application) alias(libs.plugins.kotlin.compose) alias(libs.plugins.devtools.ksp) @@ -91,6 +92,7 @@ dependencies { implementation(libs.androidx.work.runtime.ktx) ksp(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) + implementation(libs.aboutlibraries.compose) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/androidTest/java/co/adityarajput/fileflow/ExampleInstrumentedTest.kt b/app/src/androidTest/java/co/adityarajput/fileflow/ExampleInstrumentedTest.kt deleted file mode 100644 index 2b651ff..0000000 --- a/app/src/androidTest/java/co/adityarajput/fileflow/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package co.adityarajput.fileflow - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("co.adityarajput.fileflow", appContext.packageName) - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d5ba3e7..411cf24 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,15 @@ - + + + + + Unit) { ) { Icon( painterResource(R.drawable.automation), - stringResource(R.string.alttext_app_logo), + stringResource(R.string.app_logo), Modifier.size(50.dp), ) } diff --git a/app/src/main/java/co/adityarajput/fileflow/views/screens/LicensesScreen.kt b/app/src/main/java/co/adityarajput/fileflow/views/screens/LicensesScreen.kt new file mode 100644 index 0000000..150acca --- /dev/null +++ b/app/src/main/java/co/adityarajput/fileflow/views/screens/LicensesScreen.kt @@ -0,0 +1,46 @@ +package co.adityarajput.fileflow.views.screens + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import co.adityarajput.fileflow.R +import co.adityarajput.fileflow.views.components.AppBar +import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults +import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries +import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer + +@Composable +fun LicensesScreen(goBack: () -> Unit = {}) { + Scaffold( + topBar = { + AppBar( + stringResource(R.string.licenses), + true, + goBack, + ) + }, + ) { paddingValues -> + Box( + Modifier + .fillMaxSize() + .padding(paddingValues), + ) { + val libraries by produceLibraries(R.raw.aboutlibraries) + + LibrariesContainer( + libraries, + Modifier + .fillMaxSize() + .padding(dimensionResource(R.dimen.padding_small)), + showDescription = true, + textStyles = LibraryDefaults.libraryTextStyles(nameMaxLines = 2), + ) + } + } +} diff --git a/app/src/main/java/co/adityarajput/fileflow/views/screens/SettingsScreen.kt b/app/src/main/java/co/adityarajput/fileflow/views/screens/SettingsScreen.kt index 597aab7..8cb63bc 100644 --- a/app/src/main/java/co/adityarajput/fileflow/views/screens/SettingsScreen.kt +++ b/app/src/main/java/co/adityarajput/fileflow/views/screens/SettingsScreen.kt @@ -39,6 +39,7 @@ import kotlinx.coroutines.launch @SuppressLint("BatteryLife") @Composable fun SettingsScreen( + goToLicensesScreen: () -> Unit = {}, goToAboutScreen: () -> Unit = {}, goBack: () -> Unit = {}, viewModel: AppearanceViewModel, @@ -158,6 +159,7 @@ fun SettingsScreen( ) { Text( stringResource(R.string.app_theme), + Modifier.padding(end = dimensionResource(R.dimen.padding_small)), style = MaterialTheme.typography.titleSmall, ) SingleChoiceSegmentedButtonRow { @@ -186,60 +188,73 @@ fun SettingsScreen( .padding(dimensionResource(R.dimen.padding_small)), ) { val copySuccess = stringResource(R.string.copy_success) - Row( + Column( Modifier .fillMaxWidth() .padding( dimensionResource(R.dimen.padding_large), dimensionResource(R.dimen.padding_medium), - dimensionResource(R.dimen.padding_large), - dimensionResource(R.dimen.padding_small), - ) - .clickable { - scope.launch { - clipboard.setClipEntry( - ClipData.newPlainText( - "logs", - Logger.logs.joinToString("\n"), - ).toClipEntry(), - ) - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) - Toast - .makeText(context, copySuccess, Toast.LENGTH_SHORT) - .show() - } - }, - Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + ), + Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)), ) { - Icon( - painterResource(R.drawable.list_alt), - stringResource(R.string.alttext_logs), - ) - Text( - stringResource(R.string.copy_logs), - fontWeight = FontWeight.Medium, - ) - } - Row( - Modifier - .fillMaxWidth() - .padding( - dimensionResource(R.dimen.padding_large), - dimensionResource(R.dimen.padding_small), - dimensionResource(R.dimen.padding_large), - dimensionResource(R.dimen.padding_medium), + Row( + Modifier + .fillMaxWidth() + .clickable { + scope.launch { + clipboard.setClipEntry( + ClipData.newPlainText( + "logs", + Logger.logs.joinToString("\n"), + ).toClipEntry(), + ) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) + Toast + .makeText(context, copySuccess, Toast.LENGTH_SHORT) + .show() + } + }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + ) { + Icon( + painterResource(R.drawable.list_alt), + stringResource(R.string.logs), ) - .clickable { goToAboutScreen() }, - Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), - ) { - Icon( - painterResource(R.drawable.info), - stringResource(R.string.alttext_info), - ) - Text( - stringResource(R.string.about_app), - fontWeight = FontWeight.Medium, - ) + Text( + stringResource(R.string.copy_logs), + fontWeight = FontWeight.Medium, + ) + } + Row( + Modifier + .fillMaxWidth() + .clickable { goToLicensesScreen() }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + ) { + Icon( + painterResource(R.drawable.license), + stringResource(R.string.licenses), + ) + Text( + stringResource(R.string.view_licenses), + fontWeight = FontWeight.Medium, + ) + } + Row( + Modifier + .fillMaxWidth() + .clickable { goToAboutScreen() }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + ) { + Icon( + painterResource(R.drawable.info), + stringResource(R.string.info), + ) + Text( + stringResource(R.string.about_app), + fontWeight = FontWeight.Medium, + ) + } } } } 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 cf2665b..a45d1ad 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 @@ -4,7 +4,9 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -57,6 +59,7 @@ fun UpsertRuleScreen( Column( Modifier .fillMaxWidth() + .verticalScroll(rememberScrollState()) .weight(1f) .padding(dimensionResource(R.dimen.padding_small)) .padding( @@ -219,7 +222,7 @@ private fun ColumnScope.ActionPage(viewModel: UpsertRuleViewModel) { } Icon( painterResource(R.drawable.arrow_down), - stringResource(R.string.alttext_arrow_down), + stringResource(R.string.arrow_down), Modifier.align(Alignment.CenterHorizontally), ) Text( diff --git a/app/src/main/res/drawable/license.xml b/app/src/main/res/drawable/license.xml new file mode 100644 index 0000000..73d46ee --- /dev/null +++ b/app/src/main/res/drawable/license.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml deleted file mode 100644 index 545704f..0000000 --- a/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml new file mode 100644 index 0000000..85f734a --- /dev/null +++ b/app/src/main/res/values/plurals.xml @@ -0,0 +1,24 @@ + + + + %1$d execution + %1$d executions + + + + %1$d day ago + %1$d days ago + + + %1$d hour ago + %1$d hours ago + + + %1$d minute ago + %1$d minutes ago + + + %1$d second ago + %1$d seconds ago + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 780733f..9f1ad56 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,13 +1,58 @@ - FileFlow - FileFlow - 1.1.0 + FileFlow + FileFlow + 1.1.0 + + About + App logo + " scans your files periodically and organizes them according to your rules." + GPLv3
Source code available on GitHub
Visit the project wiki for more]]>
+
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: to request exemption from background usage restrictions
Other permissions may be used by libraries to manage tasks, such as executing flows periodically.]]>
+ BURG3R5]]> + + Back button + + + + + History + No executions yet. + + "1k+ days ago" + just now + + + + + Licenses + + + + Add rule No rules added.\nTap + to get started. + disabled + Execute rule + %1$s rule + Edit rule + Delete rule + + Execute this rule? + %1$s this rule? + Deletion is permanent; your rule and its stats will be lost.%1$s + " Consider disabling instead." + Execute + Enable + Disable + Delete + + MOVE COPY - disabled + + + Settings Optional Enhancements Disable optimization @@ -17,12 +62,18 @@ Light System Dark - Copy logs Logs copied to clipboard + Logs + Copy logs + View licenses + Info About FileFlow + - Add rule - Edit rule + + Cancel + Add + Save "Source: " select folder File name pattern @@ -32,10 +83,7 @@ " or " Delete original files from the source after execution "In case of multiple matches, choose: " - earliest - latest - smallest - largest + An arrow pointing downward "Destination: " File name template Enter a regex template @@ -45,58 +93,11 @@ Regex pattern contains errors Regex template contains errors Regex pattern doesn\'t match any file in the source folder - Cancel - Add - Save - - Execute rule - Execute this rule? - %1$s rule - %1$s this rule? - Delete rule - Deletion is permanent; your rule and its stats will be lost.%1$s - " Consider disabling instead." - Execute - Enable - Disable - Delete - - History - No executions yet. - - About - " scans your files periodically and organizes them according to your rules." - GPLv3
Source code available on GitHub
Visit the project wiki for more]]>
-
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: to request exemption from background usage restrictions
]]>
- BURG3R5]]> - - App logo - Back button - An arrow pointing downward - Logs - Info - - - %1$d execution - %1$d executions - - - "1k+ days ago" - - %1$d day ago - %1$d days ago - - - %1$d hour ago - %1$d hours ago - - - %1$d minute ago - %1$d minutes ago - - - %1$d second ago - %1$d seconds ago - - just now + + earliest + latest + smallest + largest + +
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml index 01207dc..1b0854f 100644 --- a/app/src/main/res/xml/backup_rules.xml +++ b/app/src/main/res/xml/backup_rules.xml @@ -1,2 +1,2 @@ - + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index f8cf996..04c46e2 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -1,4 +1,4 @@ - + diff --git a/app/src/test/java/co/adityarajput/fileflow/ExampleUnitTest.kt b/app/src/test/java/co/adityarajput/fileflow/ExampleUnitTest.kt deleted file mode 100644 index ada1746..0000000 --- a/app/src/test/java/co/adityarajput/fileflow/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package co.adityarajput.fileflow - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/build.gradle.kts b/build.gradle.kts index 020183f..edecb5f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + alias(libs.plugins.aboutlibraries.android) apply false alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.compose) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ebd8ca5..9047298 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,14 @@ [versions] -agp = "9.0.0" -kotlin = "2.3.0" +aboutlibraries = "14.0.0-b02" +agp = "9.0.1" +kotlin = "2.3.10" coreKtx = "1.17.0" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" lifecycleRuntimeKtx = "2.10.0" -activityCompose = "1.12.3" -composeBom = "2026.01.01" +activityCompose = "1.12.4" +composeBom = "2026.02.00" appcompat = "1.7.1" navigationCompose = "2.9.7" room = "2.8.4" @@ -15,7 +16,7 @@ composeMaterial = "1.5.6" kotlinxSerializationJson = "1.10.0" documentfile = "1.1.0" workRuntimeKtx = "2.11.1" -ksp = "2.3.3" +ksp = "2.3.4" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -42,8 +43,10 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" } androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workRuntimeKtx" } +aboutlibraries-compose = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "aboutlibraries" } [plugins] +aboutlibraries-android = { id = "com.mikepenz.aboutlibraries.plugin.android", version.ref = "aboutlibraries" } android-application = { id = "com.android.application", version.ref = "agp" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }