From 4c56aa2d9631fdf6d94701a19772cef8aa4ecf07 Mon Sep 17 00:00:00 2001 From: Philipp Thaler Date: Sun, 9 Nov 2025 18:18:07 +0100 Subject: [PATCH 1/5] feat: provide new settings for uploading using subfolders --- .idea/codeStyles/Project.xml | 7 +--- .../android/db/PreferenceManager.java | 1 + .../SettingsPictureUploadsFragment.kt | 26 +++++++++++++ .../SettingsPictureUploadsViewModel.kt | 13 +++++++ .../SettingsVideoUploadsViewModel.kt | 2 + opencloudApp/src/main/res/values/strings.xml | 7 ++++ .../main/res/xml/settings_picture_uploads.xml | 8 ++++ .../android/data/OpencloudDatabase.kt | 5 ++- .../opencloud/android/data/ProviderMeta.java | 2 +- .../OCLocalFolderBackupDataSource.kt | 3 ++ .../folderbackup/db/FolderBackUpEntity.kt | 3 ++ .../android/data/migrations/Migration_34.kt | 3 ++ .../android/data/migrations/Migration_44.kt | 38 +++++++++++++++++++ .../model/FolderBackUpConfiguration.kt | 18 +++++++++ 14 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 opencloudData/src/main/java/eu/opencloud/android/data/migrations/Migration_44.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index af4fbad11b..f9dba3bf89 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -25,11 +25,9 @@ - @@ -38,7 +36,6 @@ - + \ No newline at end of file diff --git a/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java b/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java index 97bdc76b28..c761651316 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java +++ b/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java @@ -61,6 +61,7 @@ public abstract class PreferenceManager { public static final String PREF__CAMERA_PICTURE_UPLOADS_LAST_SYNC = "picture_uploads_last_sync"; public static final String PREF__CAMERA_VIDEO_UPLOADS_LAST_SYNC = "video_uploads_last_sync"; public static final String PREF__CAMERA_UPLOADS_DEFAULT_PATH = "/CameraUpload"; + public static final String PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR = "picture_uploads_use_subfolders_behaviour"; public static final String PREF__LEGACY_FINGERPRINT = "set_fingerprint"; /** * Constant to access value of last path selected by the user to upload a file shared from other app. diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index e1b8a7d17a..20cb469f96 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -48,7 +48,9 @@ import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_LA import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY +import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.extensions.collectLatestLifecycleFlow import eu.opencloud.android.extensions.showAlertDialog import eu.opencloud.android.extensions.showMessageInSnackbar @@ -71,6 +73,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { private var prefPictureUploadsOnCharging: CheckBoxPreference? = null private var prefPictureUploadsSourcePath: Preference? = null private var prefPictureUploadsBehaviour: ListPreference? = null + private var prefPictureUploadsUseSubfolderBehaviour: ListPreference? = null private var prefPictureUploadsAccount: ListPreference? = null private var prefPictureUploadsLastSync: Preference? = null private var spaceId: String? = null @@ -109,6 +112,20 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { ).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } + prefPictureUploadsUseSubfolderBehaviour = findPreference(PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR)?.apply { + entries = listOf( + getString(R.string.pref_use_subfolders_behaviour_none), + getString(R.string.pref_use_subfolders_behaviour_year), + getString(R.string.pref_use_subfolders_behaviour_year_month), + getString(R.string.pref_use_subfolders_behaviour_year_month_day), + ).toTypedArray() + entryValues = listOf( + UseSubfoldersBehaviour.NONE.name, + UseSubfoldersBehaviour.YEAR.name, + UseSubfoldersBehaviour.YEAR_MONTH.name, + UseSubfoldersBehaviour.YEAR_MONTH_DAY.name + ).toTypedArray() + } prefPictureUploadsAccount = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME) val comment = getString(R.string.prefs_camera_upload_source_path_title_required) @@ -155,6 +172,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsOnWifi?.isChecked = it.wifiOnly prefPictureUploadsOnCharging?.isChecked = it.chargingOnly prefPictureUploadsBehaviour?.value = it.behavior.name + prefPictureUploadsUseSubfolderBehaviour?.value = it.useSubfoldersBehaviour.name prefPictureUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) spaceId = it.spaceId } ?: resetFields() @@ -223,6 +241,12 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { true } + prefPictureUploadsUseSubfolderBehaviour?.setOnPreferenceChangeListener { _, newValue -> + newValue as String + picturesViewModel.handleSelectUseSubfoldersBehaviour(newValue) + true + } + prefPictureUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean picturesViewModel.useWifiOnly(newValue) @@ -264,6 +288,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsSourcePath?.isEnabled = value prefPictureUploadsBehaviour?.isEnabled = value prefPictureUploadsAccount?.isEnabled = value + prefPictureUploadsUseSubfolderBehaviour?.isEnabled = value prefPictureUploadsLastSync?.isEnabled = value } @@ -274,6 +299,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsOnWifi?.isChecked = false prefPictureUploadsOnCharging?.isChecked = false prefPictureUploadsBehaviour?.value = UploadBehavior.COPY.name + prefPictureUploadsUseSubfolderBehaviour?.value = UseSubfoldersBehaviour.NONE.name prefPictureUploadsLastSync?.summary = null } diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt index c076cc325d..dfd81c1f2c 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt @@ -31,6 +31,7 @@ import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PA import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration.Companion.pictureUploadsName import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.domain.automaticuploads.usecases.GetPictureUploadsConfigurationStreamUseCase import eu.opencloud.android.domain.automaticuploads.usecases.ResetPictureUploadsUseCase import eu.opencloud.android.domain.automaticuploads.usecases.SavePictureUploadsConfigurationUseCase @@ -121,6 +122,16 @@ class SettingsPictureUploadsViewModel( } } + fun handleSelectUseSubfoldersBehaviour(behaviourString: String) { + val behaviour = UseSubfoldersBehaviour.fromString(behaviourString) + + viewModelScope.launch(coroutinesDispatcherProvider.io) { + savePictureUploadsConfigurationUseCase( + SavePictureUploadsConfigurationUseCase.Params(composePictureUploadsConfiguration(useSubfoldersBehaviour = behaviour)) + ) + } + } + fun getPictureUploadsAccount() = _pictureUploads.value?.accountName fun getPictureUploadsPath() = _pictureUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH @@ -196,6 +207,7 @@ class SettingsPictureUploadsViewModel( chargingOnly: Boolean? = _pictureUploads.value?.chargingOnly, sourcePath: String? = _pictureUploads.value?.sourcePath, behavior: UploadBehavior? = _pictureUploads.value?.behavior, + useSubfoldersBehaviour: UseSubfoldersBehaviour? = _pictureUploads.value?.useSubfoldersBehaviour, timestamp: Long? = _pictureUploads.value?.lastSyncTimestamp, spaceId: String? = _pictureUploads.value?.spaceId, ): FolderBackUpConfiguration = FolderBackUpConfiguration( @@ -205,6 +217,7 @@ class SettingsPictureUploadsViewModel( uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH, wifiOnly = wifiOnly ?: false, chargingOnly = chargingOnly ?: false, + useSubfoldersBehaviour = useSubfoldersBehaviour ?: UseSubfoldersBehaviour.YEAR, lastSyncTimestamp = timestamp ?: System.currentTimeMillis(), name = _pictureUploads.value?.name ?: pictureUploadsName, spaceId = spaceId, diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt index b9f077b8da..ecd21755b2 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt @@ -31,6 +31,7 @@ import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PA import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration.Companion.videoUploadsName import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.domain.automaticuploads.usecases.GetVideoUploadsConfigurationStreamUseCase import eu.opencloud.android.domain.automaticuploads.usecases.ResetVideoUploadsUseCase import eu.opencloud.android.domain.automaticuploads.usecases.SaveVideoUploadsConfigurationUseCase @@ -207,6 +208,7 @@ class SettingsVideoUploadsViewModel( wifiOnly = wifiOnly ?: false, chargingOnly = chargingOnly ?: false, lastSyncTimestamp = timestamp ?: System.currentTimeMillis(), + useSubfoldersBehaviour = UseSubfoldersBehaviour.NONE, name = _videoUploads.value?.name ?: videoUploadsName, spaceId = spaceId, ).also { diff --git a/opencloudApp/src/main/res/values/strings.xml b/opencloudApp/src/main/res/values/strings.xml index 35e5873b9b..f95408b7d0 100644 --- a/opencloudApp/src/main/res/values/strings.xml +++ b/opencloudApp/src/main/res/values/strings.xml @@ -580,6 +580,8 @@ Original file will be Original file will be Last synchronization + Subfolder options + Store in subfolders based on date You can update your preferences in Settings Copy file Move file @@ -587,6 +589,11 @@ kept in original folder removed from original folder + None + Year + Year/Month + Year/Month/Day + Share Share %1$s Users and Groups diff --git a/opencloudApp/src/main/res/xml/settings_picture_uploads.xml b/opencloudApp/src/main/res/xml/settings_picture_uploads.xml index 51a32a2bcf..d6c76eb273 100644 --- a/opencloudApp/src/main/res/xml/settings_picture_uploads.xml +++ b/opencloudApp/src/main/res/xml/settings_picture_uploads.xml @@ -48,6 +48,14 @@ app:negativeButtonText="" app:title="@string/prefs_camera_upload_behaviour_title" app:useSimpleSummaryProvider="true" /> + . + */ + +package eu.opencloud.android.data.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import eu.opencloud.android.data.ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour + +val MIGRATION_43_44 = object : Migration(43, 44) { + override fun migrate(database: SupportSQLiteDatabase) { + database.run { + execSQL( + "ALTER TABLE $FOLDER_BACKUP_TABLE_NAME ADD COLUMN `useSubfoldersBehaviour` TEXT NOT NULL DEFAULT ${ + UseSubfoldersBehaviour.NONE.name + }" + ) + } + } +} diff --git a/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt b/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt index 221ebb6b1f..5f1fbd1ddb 100644 --- a/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt +++ b/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt @@ -24,6 +24,7 @@ data class FolderBackUpConfiguration( val behavior: UploadBehavior, val sourcePath: String, val uploadPath: String, + val useSubfoldersBehaviour: UseSubfoldersBehaviour, val wifiOnly: Boolean, val chargingOnly: Boolean, val lastSyncTimestamp: Long, @@ -68,3 +69,20 @@ enum class UploadBehavior { } } } + +enum class UseSubfoldersBehaviour { + NONE, YEAR, YEAR_MONTH, YEAR_MONTH_DAY; + + companion object { + fun fromString(string: String): UseSubfoldersBehaviour = + if (string.equals(YEAR.name, ignoreCase = true)) { + YEAR + } else if (string.equals(YEAR_MONTH.name, ignoreCase = true)) { + YEAR_MONTH + } else if (string.equals(YEAR_MONTH_DAY.name, ignoreCase = true)) { + YEAR_MONTH_DAY + } else { + NONE + } + } +} From 520a43f7bef61bd4519bfe58e7d21f2f28ea6015 Mon Sep 17 00:00:00 2001 From: Philipp Thaler Date: Sun, 9 Nov 2025 20:39:18 +0100 Subject: [PATCH 2/5] feat: implement logic for building the upload path based on lastModifiedTime --- .../android/workers/AutomaticUploadsWorker.kt | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt b/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt index b60fba020b..9cdd45369e 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt @@ -32,23 +32,27 @@ import eu.opencloud.android.R import eu.opencloud.android.domain.UseCaseResult import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.domain.automaticuploads.usecases.GetAutomaticUploadsConfigurationUseCase import eu.opencloud.android.domain.automaticuploads.usecases.SavePictureUploadsConfigurationUseCase import eu.opencloud.android.domain.automaticuploads.usecases.SaveVideoUploadsConfigurationUseCase import eu.opencloud.android.domain.transfers.TransferRepository import eu.opencloud.android.domain.transfers.model.OCTransfer import eu.opencloud.android.domain.transfers.model.TransferStatus -import eu.opencloud.android.presentation.settings.SettingsActivity import eu.opencloud.android.domain.transfers.model.UploadEnqueuedBy +import eu.opencloud.android.presentation.settings.SettingsActivity import eu.opencloud.android.usecases.transfers.uploads.UploadFileFromContentUriUseCase import eu.opencloud.android.utils.MimetypeIconUtil import eu.opencloud.android.utils.NotificationUtils import eu.opencloud.android.utils.UPLOAD_NOTIFICATION_CHANNEL_ID import org.koin.core.component.KoinComponent import org.koin.core.component.inject - import timber.log.Timber import java.io.File +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter import java.util.Date import java.util.concurrent.TimeUnit @@ -144,9 +148,10 @@ class AutomaticUploadsWorker( showNotification(syncType, localPicturesDocumentFiles.size) for (documentFile in localPicturesDocumentFiles) { + val uploadPath = buildUploadPath(documentFile, folderBackUpConfiguration) val uploadId = storeInUploadsDatabase( documentFile = documentFile, - uploadPath = folderBackUpConfiguration.uploadPath.plus(File.separator).plus(documentFile.name), + uploadPath = uploadPath, accountName = folderBackUpConfiguration.accountName, behavior = folderBackUpConfiguration.behavior, createdByWorker = when (syncType) { @@ -157,7 +162,7 @@ class AutomaticUploadsWorker( ) enqueueSingleUpload( contentUri = documentFile.uri, - uploadPath = folderBackUpConfiguration.uploadPath.plus(File.separator).plus(documentFile.name), + uploadPath = uploadPath, lastModified = documentFile.lastModified(), behavior = folderBackUpConfiguration.behavior.toString(), accountName = folderBackUpConfiguration.accountName, @@ -169,6 +174,32 @@ class AutomaticUploadsWorker( updateTimestamp(folderBackUpConfiguration, syncType, currentTimestamp) } + private fun buildUploadPath( + documentFile: DocumentFile, + folderBackUpConfiguration: FolderBackUpConfiguration, + ): String { + val pathBuilder = StringBuilder(folderBackUpConfiguration.uploadPath.plus(File.separator)) + + val lastModifiedDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(documentFile.lastModified()), ZoneId.systemDefault()) + val yearStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("YYYY")) + val monthStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("MM")) + val dayStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("dd")) + + if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR) { + pathBuilder.append(yearStr).append(File.separator) + } + if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR_MONTH) { + pathBuilder.append(yearStr).append(File.separator) + pathBuilder.append(monthStr).append(File.separator) + } + if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR_MONTH_DAY) { + pathBuilder.append(yearStr).append(File.separator) + pathBuilder.append(monthStr).append(File.separator) + pathBuilder.append(dayStr).append(File.separator) + } + return pathBuilder.append(documentFile.name).toString() + } + private fun showNotification( syncType: SyncType, numberOfFilesToUpload: Int From 48cb390446b27112eed1d7bd3b12ffdfc78aa09c Mon Sep 17 00:00:00 2001 From: Philipp Thaler Date: Sun, 9 Nov 2025 21:18:50 +0100 Subject: [PATCH 3/5] feat: also implement logic for video uploads --- .../android/db/PreferenceManager.java | 3 ++- .../SettingsPictureUploadsFragment.kt | 4 +-- .../SettingsPictureUploadsViewModel.kt | 2 +- .../SettingsVideoUploadsFragment.kt | 26 +++++++++++++++++++ .../SettingsVideoUploadsViewModel.kt | 13 +++++++++- .../main/res/xml/settings_video_uploads.xml | 8 ++++++ 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java b/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java index c761651316..34054e96f2 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java +++ b/opencloudApp/src/main/java/eu/opencloud/android/db/PreferenceManager.java @@ -61,7 +61,8 @@ public abstract class PreferenceManager { public static final String PREF__CAMERA_PICTURE_UPLOADS_LAST_SYNC = "picture_uploads_last_sync"; public static final String PREF__CAMERA_VIDEO_UPLOADS_LAST_SYNC = "video_uploads_last_sync"; public static final String PREF__CAMERA_UPLOADS_DEFAULT_PATH = "/CameraUpload"; - public static final String PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR = "picture_uploads_use_subfolders_behaviour"; + public static final String PREF__CAMERA_PICTURE_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR = "picture_uploads_use_subfolders_behaviour"; + public static final String PREF__CAMERA_VIDEO_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR = "video_uploads_use_subfolders_behaviour"; public static final String PREF__LEGACY_FINGERPRINT = "set_fingerprint"; /** * Constant to access value of last path selected by the user to upload a file shared from other app. diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index 20cb469f96..c0c1b5b1b6 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -48,7 +48,7 @@ import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_LA import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY -import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR +import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.extensions.collectLatestLifecycleFlow @@ -112,7 +112,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { ).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } - prefPictureUploadsUseSubfolderBehaviour = findPreference(PREF__CAMERA_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR)?.apply { + prefPictureUploadsUseSubfolderBehaviour = findPreference(PREF__CAMERA_PICTURE_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR)?.apply { entries = listOf( getString(R.string.pref_use_subfolders_behaviour_none), getString(R.string.pref_use_subfolders_behaviour_year), diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt index dfd81c1f2c..754e8e9103 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt @@ -217,7 +217,7 @@ class SettingsPictureUploadsViewModel( uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH, wifiOnly = wifiOnly ?: false, chargingOnly = chargingOnly ?: false, - useSubfoldersBehaviour = useSubfoldersBehaviour ?: UseSubfoldersBehaviour.YEAR, + useSubfoldersBehaviour = useSubfoldersBehaviour ?: UseSubfoldersBehaviour.NONE, lastSyncTimestamp = timestamp ?: System.currentTimeMillis(), name = _pictureUploads.value?.name ?: pictureUploadsName, spaceId = spaceId, diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 98b6dc0cfd..1e913b3f52 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -47,8 +47,10 @@ import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_CHAR import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE +import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR import eu.opencloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour import eu.opencloud.android.extensions.collectLatestLifecycleFlow import eu.opencloud.android.extensions.showAlertDialog import eu.opencloud.android.extensions.showMessageInSnackbar @@ -71,6 +73,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { private var prefVideoUploadsOnCharging: CheckBoxPreference? = null private var prefVideoUploadsSourcePath: Preference? = null private var prefVideoUploadsBehaviour: ListPreference? = null + private var prefVideoUploadsUseSubfolderBehaviour: ListPreference? = null private var prefVideoUploadsAccount: ListPreference? = null private var prefVideoUploadsLastSync: Preference? = null private var spaceId: String? = null @@ -107,6 +110,20 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { getString(R.string.pref_behaviour_entries_remove_original_file)).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } + prefVideoUploadsUseSubfolderBehaviour = findPreference(PREF__CAMERA_VIDEO_UPLOADS_USE_SUBFOLDERS_BEHAVIOUR)?.apply { + entries = listOf( + getString(R.string.pref_use_subfolders_behaviour_none), + getString(R.string.pref_use_subfolders_behaviour_year), + getString(R.string.pref_use_subfolders_behaviour_year_month), + getString(R.string.pref_use_subfolders_behaviour_year_month_day), + ).toTypedArray() + entryValues = listOf( + UseSubfoldersBehaviour.NONE.name, + UseSubfoldersBehaviour.YEAR.name, + UseSubfoldersBehaviour.YEAR_MONTH.name, + UseSubfoldersBehaviour.YEAR_MONTH_DAY.name + ).toTypedArray() + } prefVideoUploadsAccount = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME) val comment = getString(R.string.prefs_camera_upload_source_path_title_required) @@ -153,6 +170,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsOnWifi?.isChecked = it.wifiOnly prefVideoUploadsOnCharging?.isChecked = it.chargingOnly prefVideoUploadsBehaviour?.value = it.behavior.name + prefVideoUploadsUseSubfolderBehaviour?.value = it.useSubfoldersBehaviour.name prefVideoUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) spaceId = it.spaceId } ?: resetFields() @@ -221,6 +239,12 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { true } + prefVideoUploadsUseSubfolderBehaviour?.setOnPreferenceChangeListener { _, newValue -> + newValue as String + videosViewModel.handleSelectUseSubfoldersBehaviour(newValue) + true + } + prefVideoUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean videosViewModel.useWifiOnly(newValue) @@ -262,6 +286,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsSourcePath?.isEnabled = value prefVideoUploadsBehaviour?.isEnabled = value prefVideoUploadsAccount?.isEnabled = value + prefVideoUploadsUseSubfolderBehaviour?.isEnabled = value prefVideoUploadsLastSync?.isEnabled = value } @@ -272,6 +297,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsOnWifi?.isChecked = false prefVideoUploadsOnCharging?.isChecked = false prefVideoUploadsBehaviour?.value = UploadBehavior.COPY.name + prefVideoUploadsUseSubfolderBehaviour?.value = UseSubfoldersBehaviour.NONE.name prefVideoUploadsLastSync?.summary = null } diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt index ecd21755b2..af61f926c5 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt @@ -122,6 +122,16 @@ class SettingsVideoUploadsViewModel( } } + fun handleSelectUseSubfoldersBehaviour(behaviourString: String) { + val behaviour = UseSubfoldersBehaviour.fromString(behaviourString) + + viewModelScope.launch(coroutinesDispatcherProvider.io) { + saveVideoUploadsConfigurationUseCase( + SaveVideoUploadsConfigurationUseCase.Params(composeVideoUploadsConfiguration(useSubfoldersBehaviour = behaviour)) + ) + } + } + fun getVideoUploadsAccount() = _videoUploads.value?.accountName fun getVideoUploadsPath() = _videoUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH @@ -197,6 +207,7 @@ class SettingsVideoUploadsViewModel( chargingOnly: Boolean? = _videoUploads.value?.chargingOnly, sourcePath: String? = _videoUploads.value?.sourcePath, behavior: UploadBehavior? = _videoUploads.value?.behavior, + useSubfoldersBehaviour: UseSubfoldersBehaviour? = _videoUploads.value?.useSubfoldersBehaviour, timestamp: Long? = _videoUploads.value?.lastSyncTimestamp, spaceId: String? = _videoUploads.value?.spaceId, ): FolderBackUpConfiguration = @@ -208,7 +219,7 @@ class SettingsVideoUploadsViewModel( wifiOnly = wifiOnly ?: false, chargingOnly = chargingOnly ?: false, lastSyncTimestamp = timestamp ?: System.currentTimeMillis(), - useSubfoldersBehaviour = UseSubfoldersBehaviour.NONE, + useSubfoldersBehaviour = useSubfoldersBehaviour ?: UseSubfoldersBehaviour.NONE, name = _videoUploads.value?.name ?: videoUploadsName, spaceId = spaceId, ).also { diff --git a/opencloudApp/src/main/res/xml/settings_video_uploads.xml b/opencloudApp/src/main/res/xml/settings_video_uploads.xml index dea2f60625..ed4630627e 100644 --- a/opencloudApp/src/main/res/xml/settings_video_uploads.xml +++ b/opencloudApp/src/main/res/xml/settings_video_uploads.xml @@ -46,6 +46,14 @@ app:negativeButtonText="" app:title="@string/prefs_camera_upload_behaviour_title" app:useSimpleSummaryProvider="true" /> + Date: Sun, 9 Nov 2025 22:10:41 +0100 Subject: [PATCH 4/5] feat: change settings text --- .idea/codeStyles/Project.xml | 7 +++++-- opencloudApp/src/main/res/values/strings.xml | 4 ++-- opencloudApp/src/main/res/xml/settings_picture_uploads.xml | 4 ++-- opencloudApp/src/main/res/xml/settings_video_uploads.xml | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index f9dba3bf89..af4fbad11b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -25,9 +25,11 @@ + @@ -36,6 +38,7 @@ - \ No newline at end of file + diff --git a/opencloudApp/src/main/res/values/strings.xml b/opencloudApp/src/main/res/values/strings.xml index f95408b7d0..168756d3ec 100644 --- a/opencloudApp/src/main/res/values/strings.xml +++ b/opencloudApp/src/main/res/values/strings.xml @@ -580,8 +580,8 @@ Original file will be Original file will be Last synchronization - Subfolder options - Store in subfolders based on date + Upload to Subfolders based on date + Pick subfolder structure You can update your preferences in Settings Copy file Move file diff --git a/opencloudApp/src/main/res/xml/settings_picture_uploads.xml b/opencloudApp/src/main/res/xml/settings_picture_uploads.xml index d6c76eb273..303481aa95 100644 --- a/opencloudApp/src/main/res/xml/settings_picture_uploads.xml +++ b/opencloudApp/src/main/res/xml/settings_picture_uploads.xml @@ -49,12 +49,12 @@ app:title="@string/prefs_camera_upload_behaviour_title" app:useSimpleSummaryProvider="true" /> Date: Sun, 9 Nov 2025 23:40:40 +0100 Subject: [PATCH 5/5] refactor: small changes --- .../android/workers/AutomaticUploadsWorker.kt | 30 +++++++++++-------- .../folderbackup/db/FolderBackUpEntity.kt | 2 +- .../android/data/migrations/Migration_44.kt | 20 ------------- .../model/FolderBackUpConfiguration.kt | 13 ++++---- .../testutil/OCFolderBackUpConfiguration.kt | 7 +++-- 5 files changed, 29 insertions(+), 43 deletions(-) diff --git a/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt b/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt index 9cdd45369e..4861c9922d 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/workers/AutomaticUploadsWorker.kt @@ -181,21 +181,27 @@ class AutomaticUploadsWorker( val pathBuilder = StringBuilder(folderBackUpConfiguration.uploadPath.plus(File.separator)) val lastModifiedDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(documentFile.lastModified()), ZoneId.systemDefault()) - val yearStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("YYYY")) + val yearStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("yyyy")) val monthStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("MM")) val dayStr = lastModifiedDateTime.format(DateTimeFormatter.ofPattern("dd")) - if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR) { - pathBuilder.append(yearStr).append(File.separator) - } - if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR_MONTH) { - pathBuilder.append(yearStr).append(File.separator) - pathBuilder.append(monthStr).append(File.separator) - } - if (folderBackUpConfiguration.useSubfoldersBehaviour == UseSubfoldersBehaviour.YEAR_MONTH_DAY) { - pathBuilder.append(yearStr).append(File.separator) - pathBuilder.append(monthStr).append(File.separator) - pathBuilder.append(dayStr).append(File.separator) + when (folderBackUpConfiguration.useSubfoldersBehaviour) { + UseSubfoldersBehaviour.YEAR_MONTH_DAY -> { + pathBuilder.append(yearStr).append(File.separator) + pathBuilder.append(monthStr).append(File.separator) + pathBuilder.append(dayStr).append(File.separator) + } + + UseSubfoldersBehaviour.YEAR_MONTH -> { + pathBuilder.append(yearStr).append(File.separator) + pathBuilder.append(monthStr).append(File.separator) + } + + UseSubfoldersBehaviour.YEAR -> { + pathBuilder.append(yearStr).append(File.separator) + } + + else -> {} } return pathBuilder.append(documentFile.name).toString() } diff --git a/opencloudData/src/main/java/eu/opencloud/android/data/folderbackup/db/FolderBackUpEntity.kt b/opencloudData/src/main/java/eu/opencloud/android/data/folderbackup/db/FolderBackUpEntity.kt index adfe9f024e..9efaff95d8 100644 --- a/opencloudData/src/main/java/eu/opencloud/android/data/folderbackup/db/FolderBackUpEntity.kt +++ b/opencloudData/src/main/java/eu/opencloud/android/data/folderbackup/db/FolderBackUpEntity.kt @@ -30,7 +30,7 @@ data class FolderBackUpEntity( val sourcePath: String, val uploadPath: String, val wifiOnly: Boolean, - @ColumnInfo(name = "useSubfoldersBehaviour", defaultValue = "YEAR") + @ColumnInfo(name = "useSubfoldersBehaviour", defaultValue = "NONE") val useSubfoldersBehaviour: String, val chargingOnly: Boolean, val name: String, diff --git a/opencloudData/src/main/java/eu/opencloud/android/data/migrations/Migration_44.kt b/opencloudData/src/main/java/eu/opencloud/android/data/migrations/Migration_44.kt index 70caa83113..12dcb0578e 100644 --- a/opencloudData/src/main/java/eu/opencloud/android/data/migrations/Migration_44.kt +++ b/opencloudData/src/main/java/eu/opencloud/android/data/migrations/Migration_44.kt @@ -1,23 +1,3 @@ -/** - * openCloud Android client application - * - * @author Aitor Ballesteros Pavón - * - * Copyright (C) 2023 ownCloud GmbH. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package eu.opencloud.android.data.migrations import androidx.room.migration.Migration diff --git a/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt b/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt index 5f1fbd1ddb..ebb1818481 100644 --- a/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt +++ b/opencloudDomain/src/main/java/eu/opencloud/android/domain/automaticuploads/model/FolderBackUpConfiguration.kt @@ -75,14 +75,11 @@ enum class UseSubfoldersBehaviour { companion object { fun fromString(string: String): UseSubfoldersBehaviour = - if (string.equals(YEAR.name, ignoreCase = true)) { - YEAR - } else if (string.equals(YEAR_MONTH.name, ignoreCase = true)) { - YEAR_MONTH - } else if (string.equals(YEAR_MONTH_DAY.name, ignoreCase = true)) { - YEAR_MONTH_DAY - } else { - NONE + when (string.uppercase()) { + YEAR.name -> YEAR + YEAR_MONTH.name -> YEAR_MONTH + YEAR_MONTH_DAY.name -> YEAR_MONTH_DAY + else -> NONE } } } diff --git a/opencloudTestUtil/src/main/java/eu/opencloud/android/testutil/OCFolderBackUpConfiguration.kt b/opencloudTestUtil/src/main/java/eu/opencloud/android/testutil/OCFolderBackUpConfiguration.kt index ad0e62314d..5242f460a6 100644 --- a/opencloudTestUtil/src/main/java/eu/opencloud/android/testutil/OCFolderBackUpConfiguration.kt +++ b/opencloudTestUtil/src/main/java/eu/opencloud/android/testutil/OCFolderBackUpConfiguration.kt @@ -20,10 +20,11 @@ package eu.opencloud.android.testutil -import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration -import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior import eu.opencloud.android.data.folderbackup.db.FolderBackUpEntity import eu.opencloud.android.domain.automaticuploads.model.AutomaticUploadsConfiguration +import eu.opencloud.android.domain.automaticuploads.model.FolderBackUpConfiguration +import eu.opencloud.android.domain.automaticuploads.model.UploadBehavior +import eu.opencloud.android.domain.automaticuploads.model.UseSubfoldersBehaviour val OC_BACKUP = FolderBackUpConfiguration( accountName = "", @@ -33,6 +34,7 @@ val OC_BACKUP = FolderBackUpConfiguration( wifiOnly = true, chargingOnly = true, lastSyncTimestamp = 1542628397, + useSubfoldersBehaviour = UseSubfoldersBehaviour.YEAR, name = "", spaceId = null, ) @@ -45,6 +47,7 @@ val OC_BACKUP_ENTITY = FolderBackUpEntity( wifiOnly = true, chargingOnly = true, lastSyncTimestamp = 1542628397, + useSubfoldersBehaviour = UseSubfoldersBehaviour.YEAR.name, name = "", spaceId = null, )