@@ -88,33 +88,57 @@ class FileUploadHelper {
8888 fun buildRemoteName (accountName : String , remotePath : String ): String = accountName + remotePath
8989 }
9090
91+ /* *
92+ * Retries all failed uploads across all user accounts.
93+ *
94+ * This function retrieves all uploads with the status [UploadStatus.UPLOAD_FAILED], including both
95+ * manual uploads and auto uploads. It runs in a background thread (Dispatcher.IO) and ensures
96+ * that only one retry operation runs at a time by using a semaphore to prevent concurrent execution.
97+ *
98+ * Once the failed uploads are retrieved, it calls [retryUploads], which triggers the corresponding
99+ * upload workers for each failed upload.
100+ *
101+ * The function returns `true` if there were any failed uploads to retry and the retry process was
102+ * started, or `false` if no uploads were retried.
103+ *
104+ * @param uploadsStorageManager Provides access to upload data and persistence.
105+ * @param connectivityService Checks the current network connectivity state.
106+ * @param accountManager Handles user account authentication and selection.
107+ * @param powerManagementService Ensures uploads respect power constraints.
108+ * @return `true` if any failed uploads were found and retried; `false` otherwise.
109+ */
91110 fun retryFailedUploads (
92111 uploadsStorageManager : UploadsStorageManager ,
93112 connectivityService : ConnectivityService ,
94113 accountManager : UserAccountManager ,
95114 powerManagementService : PowerManagementService
96- ) {
97- if (retryFailedUploadsSemaphore.tryAcquire()) {
98- try {
99- val failedUploads = uploadsStorageManager.failedUploads
100- if (failedUploads == null || failedUploads.isEmpty()) {
101- Log_OC .d(TAG , " Failed uploads are empty or null" )
102- return
115+ ): Boolean {
116+ if (! retryFailedUploadsSemaphore.tryAcquire()) {
117+ Log_OC .d(TAG , " skipping retryFailedUploads, already running" )
118+ return true
119+ }
120+
121+ var isUploadStarted = false
122+
123+ try {
124+ getUploadsByStatus(null , UploadStatus .UPLOAD_FAILED ) {
125+ if (it.isNotEmpty()) {
126+ isUploadStarted = true
103127 }
104128
105129 retryUploads(
106130 uploadsStorageManager,
107131 connectivityService,
108132 accountManager,
109133 powerManagementService,
110- failedUploads
134+ uploads = it
111135 )
112- } finally {
113- retryFailedUploadsSemaphore.release()
114136 }
115- } else {
116- Log_OC .d( TAG , " Skip retryFailedUploads since it is already running " )
137+ } finally {
138+ retryFailedUploadsSemaphore.release( )
117139 }
140+
141+ return isUploadStarted
118142 }
119143
120144 fun retryCancelledUploads (
@@ -123,18 +147,18 @@ class FileUploadHelper {
123147 accountManager : UserAccountManager ,
124148 powerManagementService : PowerManagementService
125149 ): Boolean {
126- val cancelledUploads = uploadsStorageManager.cancelledUploadsForCurrentAccount
127- if (cancelledUploads == null || cancelledUploads.isEmpty()) {
128- return false
150+ var result = false
151+ getUploadsByStatus(accountManager.user.accountName, UploadStatus .UPLOAD_CANCELLED ) {
152+ result = retryUploads(
153+ uploadsStorageManager,
154+ connectivityService,
155+ accountManager,
156+ powerManagementService,
157+ it
158+ )
129159 }
130160
131- return retryUploads(
132- uploadsStorageManager,
133- connectivityService,
134- accountManager,
135- powerManagementService,
136- cancelledUploads
137- )
161+ return result
138162 }
139163
140164 @Suppress(" ComplexCondition" )
@@ -143,51 +167,51 @@ class FileUploadHelper {
143167 connectivityService : ConnectivityService ,
144168 accountManager : UserAccountManager ,
145169 powerManagementService : PowerManagementService ,
146- failedUploads : Array <OCUpload >
170+ uploads : Array <OCUpload >
147171 ): Boolean {
148172 var showNotExistMessage = false
149173 val isOnline = checkConnectivity(connectivityService)
150174 val connectivity = connectivityService.connectivity
151175 val batteryStatus = powerManagementService.battery
152- val accountNames = accountManager.accounts.filter { account ->
153- accountManager.getUser(account.name).isPresent
154- }.map { account ->
155- account.name
156- }.toHashSet()
157-
158- for (failedUpload in failedUploads) {
159- if (! accountNames.contains(failedUpload.accountName)) {
160- uploadsStorageManager.removeUpload(failedUpload)
161- continue
162- }
163176
164- val uploadResult =
165- checkUploadConditions(failedUpload, connectivity, batteryStatus, powerManagementService, isOnline)
177+ val uploadsToRetry = mutableListOf<Long >()
178+
179+ for (upload in uploads) {
180+ val uploadResult = checkUploadConditions(
181+ upload,
182+ connectivity,
183+ batteryStatus,
184+ powerManagementService,
185+ isOnline
186+ )
166187
167188 if (uploadResult != UploadResult .UPLOADED ) {
168- if (failedUpload .lastResult != uploadResult) {
189+ if (upload .lastResult != uploadResult) {
169190 // Setting Upload status else cancelled uploads will behave wrong, when retrying
170191 // Needs to happen first since lastResult wil be overwritten by setter
171- failedUpload .uploadStatus = UploadStatus .UPLOAD_FAILED
192+ upload .uploadStatus = UploadStatus .UPLOAD_FAILED
172193
173- failedUpload .lastResult = uploadResult
174- uploadsStorageManager.updateUpload(failedUpload )
194+ upload .lastResult = uploadResult
195+ uploadsStorageManager.updateUpload(upload )
175196 }
176197 if (uploadResult == UploadResult .FILE_NOT_FOUND ) {
177198 showNotExistMessage = true
178199 }
179200 continue
180201 }
181202
182- failedUpload.uploadStatus = UploadStatus .UPLOAD_IN_PROGRESS
183- uploadsStorageManager.updateUpload(failedUpload)
203+ // Only uploads that passed checks get marked in progress and are collected for scheduling
204+ upload.uploadStatus = UploadStatus .UPLOAD_IN_PROGRESS
205+ uploadsStorageManager.updateUpload(upload)
206+ uploadsToRetry.add(upload.uploadId)
184207 }
185208
186- accountNames.forEach { accountName ->
187- val user = accountManager.getUser(accountName)
188- if (user.isPresent) {
189- backgroundJobManager.startFilesUploadJob(user.get(), failedUploads.getUploadIds(), false )
190- }
209+ if (uploadsToRetry.isNotEmpty()) {
210+ backgroundJobManager.startFilesUploadJob(
211+ accountManager.user,
212+ uploadsToRetry.toLongArray(),
213+ false
214+ )
191215 }
192216
193217 return showNotExistMessage
@@ -235,21 +259,35 @@ class FileUploadHelper {
235259 }
236260 }
237261
262+ /* *
263+ * Retrieves uploads filtered by their status, optionally for a specific account.
264+ *
265+ * This function queries the uploads database asynchronously to obtain a list of uploads
266+ * that match the specified [status]. If an [accountName] is provided, only uploads
267+ * belonging to that account are retrieved. If [accountName] is `null`, uploads with the
268+ * given [status] from **all user accounts** are returned.
269+ *
270+ * Once the uploads are fetched, the [onCompleted] callback is invoked with the resulting array.
271+ *
272+ * @param accountName The name of the account to filter uploads by.
273+ * If `null`, uploads matching the given [status] from all accounts are returned.
274+ * @param status The [UploadStatus] to filter uploads by (e.g., `UPLOAD_FAILED`).
275+ * @param nameCollisionPolicy The [NameCollisionPolicy] to filter uploads by (e.g., `SKIP`).
276+ * @param onCompleted A callback invoked with the resulting array of [OCUpload] objects.
277+ */
238278 fun getUploadsByStatus (
239- accountName : String ,
279+ accountName : String? ,
240280 status : UploadStatus ,
241281 nameCollisionPolicy : NameCollisionPolicy ? = null,
242282 onCompleted : (Array <OCUpload >) -> Unit
243283 ) {
244284 ioScope.launch {
245- val result = uploadsStorageManager.uploadDao
246- .getUploadsByStatus(
247- accountName,
248- status.value,
249- nameCollisionPolicy?.serialize()
250- )
251- .map { it.toOCUpload(null ) }
252- .toTypedArray()
285+ val dao = uploadsStorageManager.uploadDao
286+ val result = if (accountName != null ) {
287+ dao.getUploadsByAccountNameAndStatus(accountName, status.value, nameCollisionPolicy?.serialize())
288+ } else {
289+ dao.getUploadsByStatus(status.value, nameCollisionPolicy?.serialize())
290+ }.map { it.toOCUpload(null ) }.toTypedArray()
253291 onCompleted(result)
254292 }
255293 }
0 commit comments