Skip to content

Commit 5fdcc2d

Browse files
committed
do not show all sync conflict notifications during app launch
Signed-off-by: alperozturk <[email protected]>
1 parent ceeb4b8 commit 5fdcc2d

File tree

5 files changed

+129
-0
lines changed

5 files changed

+129
-0
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@
288288
android:exported="false"
289289
tools:replace="android:exported" />
290290

291+
<receiver
292+
android:name="com.nextcloud.client.notifications.action.SyncConflictNotificationBroadcastReceiver"
293+
android:exported="false" />
291294
<receiver
292295
android:name="com.nextcloud.client.jobs.offlineOperations.receiver.OfflineOperationReceiver"
293296
android:exported="false" />

app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.nextcloud.client.device.BatteryStatus
1919
import com.nextcloud.client.device.PowerManagementService
2020
import com.nextcloud.client.jobs.BackgroundJobManager
2121
import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.currentUploadFileOperation
22+
import com.nextcloud.client.notifications.AppWideNotificationManager
2223
import com.nextcloud.client.network.Connectivity
2324
import com.nextcloud.client.network.ConnectivityService
2425
import com.nextcloud.utils.extensions.getUploadIds
@@ -170,13 +171,20 @@ class FileUploadHelper {
170171
uploads: Array<OCUpload>
171172
): Boolean {
172173
var showNotExistMessage = false
174+
var showSyncConflictNotification = false
173175
val isOnline = checkConnectivity(connectivityService)
174176
val connectivity = connectivityService.connectivity
175177
val batteryStatus = powerManagementService.battery
176178

177179
val uploadsToRetry = mutableListOf<Long>()
178180

179181
for (upload in uploads) {
182+
if (upload.lastResult == UploadResult.SYNC_CONFLICT) {
183+
Log_OC.d(TAG, "retry upload skipped, sync conflict: ${upload.remotePath}")
184+
showSyncConflictNotification = true
185+
continue
186+
}
187+
180188
val uploadResult = checkUploadConditions(
181189
upload,
182190
connectivity,
@@ -214,6 +222,10 @@ class FileUploadHelper {
214222
)
215223
}
216224

225+
if (showSyncConflictNotification) {
226+
AppWideNotificationManager.showSyncConflictNotification(MainApp.getAppContext())
227+
}
228+
217229
return showNotExistMessage
218230
}
219231

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.client.notifications
9+
10+
import android.app.PendingIntent
11+
import android.content.Context
12+
import android.content.Intent
13+
import androidx.core.app.NotificationCompat
14+
import androidx.core.app.NotificationManagerCompat
15+
import com.nextcloud.client.notifications.action.SyncConflictNotificationBroadcastReceiver
16+
import com.owncloud.android.R
17+
import com.owncloud.android.ui.activity.UploadListActivity
18+
import com.owncloud.android.ui.notifications.NotificationUtils
19+
20+
/**
21+
* Responsible for showing **app-wide notifications** in the app.
22+
*
23+
* This manager provides a centralized place to create and display notifications
24+
* that are not tied to a specific screen or feature.
25+
*
26+
*/
27+
object AppWideNotificationManager {
28+
29+
private const val SYNC_CONFLICT_NOTIFICATION_INTENT_REQ_CODE = 16
30+
private const val SYNC_CONFLICT_NOTIFICATION_INTENT_ACTION_REQ_CODE = 17
31+
32+
private const val SYNC_CONFLICT_NOTIFICATION_ID = 112
33+
34+
fun showSyncConflictNotification(context: Context) {
35+
val intent = Intent(context, UploadListActivity::class.java).apply {
36+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
37+
}
38+
39+
val pendingIntent = PendingIntent.getActivity(
40+
context,
41+
SYNC_CONFLICT_NOTIFICATION_INTENT_REQ_CODE,
42+
intent,
43+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
44+
)
45+
46+
val actionIntent = Intent(context, SyncConflictNotificationBroadcastReceiver::class.java).apply {
47+
putExtra(SyncConflictNotificationBroadcastReceiver.NOTIFICATION_ID, SYNC_CONFLICT_NOTIFICATION_ID)
48+
}
49+
50+
val actionPendingIntent = PendingIntent.getBroadcast(
51+
context,
52+
SYNC_CONFLICT_NOTIFICATION_INTENT_ACTION_REQ_CODE,
53+
actionIntent,
54+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
55+
)
56+
57+
val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
58+
.setSmallIcon(R.drawable.uploads)
59+
.setContentTitle(context.getString(R.string.uploader_upload_failed_sync_conflict_error))
60+
.setContentText(context.getString(R.string.upload_conflict_message))
61+
.setStyle(
62+
NotificationCompat.BigTextStyle()
63+
.bigText(context.getString(R.string.upload_conflict_message))
64+
)
65+
.addAction(
66+
R.drawable.ic_cloud_upload,
67+
context.getString(R.string.upload_list_resolve_conflict),
68+
actionPendingIntent
69+
)
70+
.setContentIntent(pendingIntent)
71+
.setAutoCancel(true)
72+
.setOnlyAlertOnce(true)
73+
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
74+
.build()
75+
76+
NotificationManagerCompat.from(context)
77+
.notify(SYNC_CONFLICT_NOTIFICATION_ID, notification)
78+
}
79+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.client.notifications.action
9+
10+
import android.content.BroadcastReceiver
11+
import android.content.Context
12+
import android.content.Intent
13+
import androidx.core.app.NotificationManagerCompat
14+
import com.owncloud.android.ui.activity.UploadListActivity
15+
16+
class SyncConflictNotificationBroadcastReceiver : BroadcastReceiver() {
17+
companion object {
18+
const val NOTIFICATION_ID = "NOTIFICATION_ID"
19+
}
20+
21+
override fun onReceive(context: Context, intent: Intent) {
22+
val notificationId = intent.getIntExtra(NOTIFICATION_ID, -1)
23+
24+
if (notificationId != -1) {
25+
NotificationManagerCompat.from(context).cancel(notificationId)
26+
}
27+
28+
val intent = Intent(context, UploadListActivity::class.java).apply {
29+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
30+
}
31+
context.startActivity(intent)
32+
}
33+
}

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,4 +1474,6 @@
14741474

14751475
<string name="failed_to_create_conflict_dialog">Failed to create conflict dialog</string>
14761476
<string name="editor_web_view_cannot_open_file">Cannot open file chooser</string>
1477+
1478+
<string name="upload_conflict_message">Upload conflicts detected. Open uploads to resolve</string>
14771479
</resources>

0 commit comments

Comments
 (0)