@@ -31,35 +31,67 @@ import java.io.File
3131object UploadErrorNotificationManager {
3232 private const val TAG = " UploadErrorNotificationManager"
3333
34+ /* *
35+ * Processes the result of an upload operation and manages error notifications.
36+ * * It filters out successful or silent results and handles [ResultCode.SYNC_CONFLICT]
37+ * by checking if the remote file is identical. If it's a "real" conflict or error,
38+ * it displays a notification with relevant actions (e.g., Resolve Conflict, Pause, Cancel).
39+ *
40+ * @param onSameFileConflict Triggered only if a 409 Conflict occurs but files are identical.
41+ */
42+ @Suppress(" ReturnCount" )
3443 suspend fun handleResult (
3544 context : Context ,
3645 notificationManager : WorkerNotificationManager ,
3746 operation : UploadFileOperation ,
3847 result : RemoteOperationResult <Any ?>,
39- showSameFileAlreadyExistsNotification : suspend () -> Unit = {}
48+ onSameFileConflict : suspend () -> Unit = {}
4049 ) {
4150 Log_OC .d(TAG , " handle upload result with result code: " + result.code)
4251
43- val notification = withContext(Dispatchers .IO ) {
44- val isSameFileOnRemote = FileUploadHelper .instance().isSameFileOnRemote(
45- operation.user,
46- File (operation.storagePath),
47- operation.remotePath,
48- context
49- )
52+ if (result.isSuccess || result.isCancelled || operation.isMissingPermissionThrown) {
53+ Log_OC .d(TAG , " operation is successful, cancelled or lack of storage permission, notification skipped" )
54+ return
55+ }
5056
51- getNotification(
52- isSameFileOnRemote,
53- context,
54- notificationManager.notificationBuilder,
55- operation,
56- result,
57- notifyOnSameFileExists = {
58- showSameFileAlreadyExistsNotification()
59- operation.handleLocalBehaviour()
60- }
61- )
62- } ? : return
57+ val silentCodes = setOf (
58+ ResultCode .DELAYED_FOR_WIFI ,
59+ ResultCode .DELAYED_FOR_CHARGING ,
60+ ResultCode .DELAYED_IN_POWER_SAVE_MODE ,
61+ ResultCode .LOCAL_FILE_NOT_FOUND ,
62+ ResultCode .LOCK_FAILED
63+ )
64+
65+ if (result.code in silentCodes) {
66+ Log_OC .d(TAG , " silent error code, notification skipped" )
67+ return
68+ }
69+
70+ // Do not show an error notification when uploading the same file again (manual uploads only).
71+ if (result.code == ResultCode .SYNC_CONFLICT ) {
72+ val isSameFile = withContext(Dispatchers .IO ) {
73+ FileUploadHelper .instance().isSameFileOnRemote(
74+ operation.user,
75+ File (operation.storagePath),
76+ operation.remotePath,
77+ context
78+ )
79+ }
80+
81+ if (isSameFile) {
82+ Log_OC .w(TAG , " exact same file already exists on remote, error notification skipped" )
83+ onSameFileConflict()
84+ return
85+ }
86+ }
87+
88+ // now we can show error notification
89+ val notification = getNotification(
90+ context,
91+ notificationManager.notificationBuilder,
92+ operation,
93+ result
94+ )
6395
6496 Log_OC .d(TAG , " 🔔" + " notification created" )
6597
@@ -72,19 +104,12 @@ object UploadErrorNotificationManager {
72104 }
73105 }
74106
75- private suspend fun getNotification (
76- isSameFileOnRemote : Boolean ,
107+ private fun getNotification (
77108 context : Context ,
78109 builder : NotificationCompat .Builder ,
79110 operation : UploadFileOperation ,
80- result : RemoteOperationResult <Any ?>,
81- notifyOnSameFileExists : suspend () -> Unit
82- ): Notification ? {
83- if (! shouldShowConflictDialog(isSameFileOnRemote, operation, result, notifyOnSameFileExists)) {
84- Log_OC .d(TAG , " no need to show conflict resolve notification" )
85- return null
86- }
87-
111+ result : RemoteOperationResult <Any ?>
112+ ): Notification {
88113 val textId = result.code.toFailedResultTitleId()
89114 val errorMessage = ErrorMessageAdapter .getErrorCauseMessage(result, operation, context.resources)
90115
@@ -97,19 +122,19 @@ object UploadErrorNotificationManager {
97122 setProgress(0 , 0 , false )
98123 clearActions()
99124
100- result.code.takeIf { it == ResultCode .SYNC_CONFLICT }?.let {
125+ // actions for all error types
126+ addAction(UploadBroadcastAction .PauseAndCancel (operation).pauseAction(context))
127+ addAction(UploadBroadcastAction .PauseAndCancel (operation).cancelAction(context))
128+
129+ if (result.code == ResultCode .SYNC_CONFLICT ) {
101130 addAction(
102131 R .drawable.ic_cloud_upload,
103132 context.getString(R .string.upload_list_resolve_conflict),
104133 conflictResolvePendingIntent(context, operation)
105134 )
106135 }
107136
108- addAction(UploadBroadcastAction .PauseAndCancel (operation).pauseAction(context))
109-
110- addAction(UploadBroadcastAction .PauseAndCancel (operation).cancelAction(context))
111-
112- result.code.takeIf { it == ResultCode .UNAUTHORIZED }?.let {
137+ if (result.code == ResultCode .UNAUTHORIZED ) {
113138 setContentIntent(credentialPendingIntent(context, operation))
114139 }
115140 }.build()
@@ -162,36 +187,4 @@ object UploadErrorNotificationManager {
162187 PendingIntent .FLAG_IMMUTABLE
163188 )
164189 }
165-
166- @Suppress(" ReturnCount" , " ComplexCondition" )
167- private suspend fun shouldShowConflictDialog (
168- isSameFileOnRemote : Boolean ,
169- operation : UploadFileOperation ,
170- result : RemoteOperationResult <Any ?>,
171- notifyOnSameFileExists : suspend () -> Unit
172- ): Boolean {
173- if (result.isSuccess ||
174- result.isCancelled ||
175- result.code == ResultCode .USER_CANCELLED ||
176- operation.isMissingPermissionThrown
177- ) {
178- Log_OC .w(TAG , " operation is successful, cancelled or lack of storage permission" )
179- return false
180- }
181-
182- if (result.code == ResultCode .SYNC_CONFLICT ) {
183- if (isSameFileOnRemote) {
184- Log_OC .w(TAG , " same file exists on remote" )
185- notifyOnSameFileExists()
186- }
187-
188- return false
189- }
190-
191- val delayedCodes =
192- setOf (ResultCode .DELAYED_FOR_WIFI , ResultCode .DELAYED_FOR_CHARGING , ResultCode .DELAYED_IN_POWER_SAVE_MODE )
193- val invalidCodes = setOf (ResultCode .LOCAL_FILE_NOT_FOUND , ResultCode .LOCK_FAILED )
194-
195- return result.code !in delayedCodes && result.code !in invalidCodes
196- }
197190}
0 commit comments