Skip to content

Commit 137b4eb

Browse files
Merge pull request nextcloud#16500 from nextcloud/backport/16393/stable-3.36
[stable-3.36] fix(auto-upload): calls
2 parents e9b9e93 + b058cc8 commit 137b4eb

File tree

13 files changed

+127
-210
lines changed

13 files changed

+127
-210
lines changed

app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ interface BackgroundJobManager {
120120

121121
fun startImmediateFilesExportJob(files: Collection<OCFile>): LiveData<JobInfo?>
122122

123-
fun schedulePeriodicFilesSyncJob(syncedFolder: SyncedFolder)
124-
125-
fun startAutoUploadImmediately(
123+
fun startAutoUpload(
126124
syncedFolder: SyncedFolder,
127125
overridePowerSaving: Boolean = false,
128126
contentUris: Array<String?> = arrayOf()

app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import com.owncloud.android.operations.DownloadType
4343
import kotlinx.coroutines.CoroutineScope
4444
import kotlinx.coroutines.Dispatchers
4545
import kotlinx.coroutines.launch
46+
import java.time.Duration
4647
import java.util.Date
4748
import java.util.UUID
4849
import java.util.concurrent.TimeUnit
@@ -101,8 +102,6 @@ internal class BackgroundJobManagerImpl(
101102

102103
const val JOB_TEST = "test_job"
103104

104-
const val MAX_CONTENT_TRIGGER_DELAY_MS = 10000L
105-
106105
const val TAG_PREFIX_NAME = "name"
107106
const val TAG_PREFIX_USER = "user"
108107
const val TAG_PREFIX_CLASS = "class"
@@ -269,13 +268,15 @@ internal class BackgroundJobManagerImpl(
269268
return workInfo.map { it -> it.map { fromWorkInfo(it) ?: JobInfo() }.sortedBy { it.started }.reversed() }
270269
}
271270

271+
@Suppress("MagicNumber")
272272
override fun scheduleContentObserverJob() {
273273
val constrains = Constraints.Builder()
274274
.addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true)
275275
.addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
276276
.addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true)
277277
.addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true)
278-
.setTriggerContentMaxDelay(MAX_CONTENT_TRIGGER_DELAY_MS, TimeUnit.MILLISECONDS)
278+
.setTriggerContentUpdateDelay(Duration.ofSeconds(5))
279+
.setTriggerContentMaxDelay(Duration.ofSeconds(10))
279280
.build()
280281

281282
val request = oneTimeRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER)
@@ -477,40 +478,7 @@ internal class BackgroundJobManagerImpl(
477478
)
478479
}
479480

480-
override fun schedulePeriodicFilesSyncJob(syncedFolder: SyncedFolder) {
481-
val syncedFolderID = syncedFolder.id
482-
483-
val arguments = Data.Builder()
484-
.putLong(AutoUploadWorker.SYNCED_FOLDER_ID, syncedFolderID)
485-
.build()
486-
487-
val constraints = Constraints.Builder()
488-
.setRequiredNetworkType(NetworkType.CONNECTED)
489-
.setRequiresCharging(syncedFolder.isChargingOnly)
490-
.build()
491-
492-
val request = periodicRequestBuilder(
493-
jobClass = AutoUploadWorker::class,
494-
jobName = JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID,
495-
intervalMins = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES,
496-
constraints = constraints
497-
)
498-
.setBackoffCriteria(
499-
BackoffPolicy.LINEAR,
500-
DEFAULT_BACKOFF_CRITERIA_DELAY_SEC,
501-
TimeUnit.SECONDS
502-
)
503-
.setInputData(arguments)
504-
.build()
505-
506-
workManager.enqueueUniquePeriodicWork(
507-
JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID,
508-
ExistingPeriodicWorkPolicy.KEEP,
509-
request
510-
)
511-
}
512-
513-
override fun startAutoUploadImmediately(
481+
override fun startAutoUpload(
514482
syncedFolder: SyncedFolder,
515483
overridePowerSaving: Boolean,
516484
contentUris: Array<String?>
@@ -533,6 +501,7 @@ internal class BackgroundJobManagerImpl(
533501
jobName = JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID
534502
)
535503
.setInputData(arguments)
504+
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
536505
.setConstraints(constraints)
537506
.setBackoffCriteria(
538507
BackoffPolicy.LINEAR,

app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,25 @@
77
*/
88
package com.nextcloud.client.jobs
99

10-
import android.app.Notification
1110
import android.content.Context
12-
import androidx.core.app.NotificationCompat
1311
import androidx.work.CoroutineWorker
1412
import androidx.work.WorkerParameters
1513
import com.nextcloud.client.device.PowerManagementService
16-
import com.nextcloud.utils.ForegroundServiceHelper
17-
import com.owncloud.android.R
18-
import com.owncloud.android.datamodel.ForegroundServiceType
1914
import com.owncloud.android.datamodel.SyncedFolderProvider
2015
import com.owncloud.android.lib.common.utils.Log_OC
21-
import com.owncloud.android.ui.notifications.NotificationUtils
2216
import com.owncloud.android.utils.FilesSyncHelper
2317
import kotlinx.coroutines.Dispatchers
2418
import kotlinx.coroutines.withContext
2519

2620
/**
2721
* This work is triggered when OS detects change in media folders.
2822
*
29-
* It fires media detection job and sync job and finishes immediately.
23+
* It fires media detection worker and auto upload worker and finishes immediately.
3024
*
31-
* This job must not be started on API < 24.
3225
*/
3326
@Suppress("TooGenericExceptionCaught")
3427
class ContentObserverWork(
35-
private val context: Context,
28+
context: Context,
3629
private val params: WorkerParameters,
3730
private val syncedFolderProvider: SyncedFolderProvider,
3831
private val powerManagementService: PowerManagementService,
@@ -41,8 +34,6 @@ class ContentObserverWork(
4134

4235
companion object {
4336
private const val TAG = "🔍" + "ContentObserverWork"
44-
private const val CHANNEL_ID = NotificationUtils.NOTIFICATION_CHANNEL_CONTENT_OBSERVER
45-
private const val NOTIFICATION_ID = 774
4637
}
4738

4839
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
@@ -53,10 +44,6 @@ class ContentObserverWork(
5344
try {
5445
if (params.triggeredContentUris.isNotEmpty()) {
5546
Log_OC.d(TAG, "📸 content observer detected file changes.")
56-
57-
val notificationTitle = context.getString(R.string.content_observer_work_notification_title)
58-
val notification = createNotification(notificationTitle)
59-
updateForegroundInfo(notification)
6047
checkAndTriggerAutoUpload()
6148

6249
// prevent worker fail because of another worker
@@ -69,46 +56,19 @@ class ContentObserverWork(
6956
Log_OC.d(TAG, "⚠️ triggeredContentUris is empty — nothing to sync.")
7057
}
7158

72-
rescheduleSelf()
73-
7459
val result = Result.success()
7560
backgroundJobManager.logEndOfWorker(workerName, result)
7661
Log_OC.d(TAG, "finished")
7762
result
7863
} catch (e: Exception) {
7964
Log_OC.e(TAG, "❌ Exception in ContentObserverWork: ${e.message}", e)
8065
Result.retry()
66+
} finally {
67+
Log_OC.d(TAG, "🔄" + "re-scheduling job")
68+
backgroundJobManager.scheduleContentObserverJob()
8169
}
8270
}
8371

84-
private suspend fun updateForegroundInfo(notification: Notification) {
85-
val foregroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo(
86-
NOTIFICATION_ID,
87-
notification,
88-
ForegroundServiceType.DataSync
89-
)
90-
setForeground(foregroundInfo)
91-
}
92-
93-
private fun createNotification(title: String): Notification = NotificationCompat.Builder(context, CHANNEL_ID)
94-
.setContentTitle(title)
95-
.setSmallIcon(R.drawable.ic_find_in_page)
96-
.setOngoing(true)
97-
.setSound(null)
98-
.setVibrate(null)
99-
.setOnlyAlertOnce(true)
100-
.setPriority(NotificationCompat.PRIORITY_LOW)
101-
.setSilent(true)
102-
.build()
103-
104-
/**
105-
* Re-schedules this observer to ensure continuous monitoring of media changes.
106-
*/
107-
private fun rescheduleSelf() {
108-
Log_OC.d(TAG, "🔁 Rescheduling ContentObserverWork for continued observation.")
109-
backgroundJobManager.scheduleContentObserverJob()
110-
}
111-
11272
private suspend fun checkAndTriggerAutoUpload() = withContext(Dispatchers.IO) {
11373
if (powerManagementService.isPowerSavingEnabled) {
11474
Log_OC.w(TAG, "⚡ Power saving mode active — skipping file sync.")
@@ -124,15 +84,15 @@ class ContentObserverWork(
12484
val contentUris = params.triggeredContentUris.map { uri ->
12585
// adds uri strings e.g. content://media/external/images/media/2281
12686
uri.toString()
127-
}.toTypedArray()
87+
}.toTypedArray<String?>()
12888
Log_OC.d(TAG, "📄 Content uris detected")
12989

13090
try {
131-
FilesSyncHelper.startAutoUploadImmediatelyWithContentUris(
91+
FilesSyncHelper.startAutoUploadForEnabledSyncedFolders(
13292
syncedFolderProvider,
13393
backgroundJobManager,
134-
false,
135-
contentUris
94+
contentUris,
95+
false
13696
)
13797
Log_OC.d(TAG, "✅ auto upload triggered successfully for ${contentUris.size} file(s).")
13898
} catch (e: Exception) {

app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ class AutoUploadHelper {
3131
}
3232

3333
fun insertEntries(folder: SyncedFolder, repository: FileSystemRepository) {
34-
val enabledTimestampMs = folder.enabledTimestampMs
35-
if (!folder.isEnabled || (!folder.isExisting && enabledTimestampMs < 0)) {
36-
Log_OC.w(
37-
TAG,
38-
"Skipping insertDBEntries: enabled=${folder.isEnabled}, " +
39-
"exists=${folder.isExisting}, enabledTs=$enabledTimestampMs"
40-
)
41-
return
42-
}
43-
4434
when (folder.type) {
4535
MediaFolderType.IMAGE -> {
4636
repository.insertFromUri(MediaStore.Images.Media.INTERNAL_CONTENT_URI, folder)

app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,6 @@ class AutoUploadWorker(
191191
val currentTime = System.currentTimeMillis()
192192
val passedScanInterval = totalScanInterval <= currentTime
193193

194-
Log_OC.d(TAG, "lastScanTimestampMs: " + syncedFolder.lastScanTimestampMs)
195-
Log_OC.d(TAG, "totalScanInterval: $totalScanInterval")
196-
Log_OC.d(TAG, "currentTime: $currentTime")
197-
Log_OC.d(TAG, "passedScanInterval: $passedScanInterval")
198-
199194
if (!passedScanInterval && contentUris.isNullOrEmpty() && !overridePowerSaving) {
200195
Log_OC.w(
201196
TAG,
@@ -204,6 +199,8 @@ class AutoUploadWorker(
204199
return true
205200
}
206201

202+
Log_OC.d(TAG, "starting ...")
203+
207204
return false
208205
}
209206

@@ -213,6 +210,8 @@ class AutoUploadWorker(
213210
*/
214211
@Suppress("MagicNumber", "TooGenericExceptionCaught")
215212
private suspend fun collectFileChangesFromContentObserverWork(contentUris: Array<String>?) = try {
213+
Log_OC.d(TAG, "collecting file changes")
214+
216215
withContext(Dispatchers.IO) {
217216
if (contentUris.isNullOrEmpty()) {
218217
helper.insertEntries(syncedFolder, repository)
@@ -295,7 +294,7 @@ class AutoUploadWorker(
295294
Log_OC.w(TAG, "no more files to upload at lastId: $lastId")
296295
break
297296
}
298-
Log_OC.d(TAG, "Processing batch: lastId=$lastId, count=${filePathsWithIds.size}")
297+
Log_OC.d(TAG, "started, processing batch: lastId=$lastId, count=${filePathsWithIds.size}")
299298

300299
filePathsWithIds.forEachIndexed { batchIndex, (path, id) ->
301300
val file = File(path)

app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,7 @@ default void onDarkThemeModeChanged(DarkMode mode) {
396396

397397
String getLastDisplayedAccountName();
398398
void setLastDisplayedAccountName(String lastDisplayedAccountName);
399+
400+
boolean startAutoUploadOnStart();
401+
void setLastAutoUploadOnStartTime(long timeInMillisecond);
399402
}

app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,19 @@ public final class AppPreferencesImpl implements AppPreferences {
111111

112112
private static final String PREF_LAST_DISPLAYED_ACCOUNT_NAME = "last_displayed_user";
113113

114+
private static final String AUTO_PREF__LAST_AUTO_UPLOAD_ON_START = "last_auto_upload_on_start";
115+
116+
114117
private static final String LOG_ENTRY = "log_entry";
115118

116119
private final Context context;
117120
private final SharedPreferences preferences;
118121
private final UserAccountManager userAccountManager;
119122
private final ListenerRegistry listeners;
120123

124+
private static final int AUTO_UPLOAD_ON_START_DEBOUNCE_IN_MINUTES = 10;
125+
private static final long AUTO_UPLOAD_ON_START_DEBOUNCE_MS = AUTO_UPLOAD_ON_START_DEBOUNCE_IN_MINUTES * 60 * 1000L;
126+
121127
/**
122128
* Adapter delegating raw {@link SharedPreferences.OnSharedPreferenceChangeListener} calls with key-value pairs to
123129
* respective {@link com.nextcloud.client.preferences.AppPreferences.Listener} method.
@@ -849,4 +855,16 @@ public String getLastDisplayedAccountName() {
849855
public void setLastDisplayedAccountName(String lastDisplayedAccountName) {
850856
preferences.edit().putString(PREF_LAST_DISPLAYED_ACCOUNT_NAME, lastDisplayedAccountName).apply();
851857
}
858+
859+
@Override
860+
public boolean startAutoUploadOnStart() {
861+
long lastRunTime = preferences.getLong(AUTO_PREF__LAST_AUTO_UPLOAD_ON_START, 0L);
862+
long now = System.currentTimeMillis();
863+
return lastRunTime == 0L || (now - lastRunTime) >= AUTO_UPLOAD_ON_START_DEBOUNCE_MS;
864+
}
865+
866+
@Override
867+
public void setLastAutoUploadOnStartTime(long timeInMillisecond) {
868+
preferences.edit().putLong(AUTO_PREF__LAST_AUTO_UPLOAD_ON_START, timeInMillisecond).apply();
869+
}
852870
}

0 commit comments

Comments
 (0)