From 60a2c23db40c3df3039d471d80153f41e4e7d5e9 Mon Sep 17 00:00:00 2001 From: Krainov Gleb Date: Mon, 12 Oct 2026 08:36:10 +0000 Subject: [PATCH] fix(auto-upload): start auto upload right after resuming global upload pause Resuming the global upload pause on the uploads screen only restarted FileUploadWorker. AutoUploadWorker stops without uploading while the pause is active, so its folders stayed idle until the next periodic AutoUploadRescanWorker run. Start the auto upload of all enabled synced folders when the pause is lifted. FilesSyncHelper.startAutoUploadForEnabledSyncedFolders is used instead of startAutoUploadForEnabledSyncedFoldersIfNotActive: the latter calls isAutoUploadScheduled, which blocks on WorkManager's ListenableFuture, and this code runs on the main thread from a menu click. The chosen method enqueues with ExistingWorkPolicy.KEEP when power saving is not overridden, so a worker that is already running or enqueued is left alone and no upload is cancelled mid transfer. A worker that stopped because of the pause has finished, so a new one is enqueued. Add an injectable FileUploadHelper provider to UploadListMenuProvider so the menu logic can be unit tested without the application component. Signed-off-by: Krainov Gleb Assisted-by: Claude-Code:claude-opus-5-5 --- .../fragment/uploadList/UploadListFragment.kt | 4 +- .../uploadList/UploadListMenuProvider.kt | 16 ++++- .../uploadList/UploadListMenuProviderTest.kt | 63 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 app/src/test/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProviderTest.kt diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListFragment.kt index b94de86606ce..c1de65f815f9 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListFragment.kt @@ -253,7 +253,9 @@ class UploadListFragment : activity, preferences, userAccountManager, - uploadsStorageManager + uploadsStorageManager, + syncedFolderProvider, + backgroundJobManager ) { uploadListAdapter.notifyDataSetChanged() } activity.addMenuProvider(menuProvider, viewLifecycleOwner, Lifecycle.State.RESUMED) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProvider.kt b/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProvider.kt index da637f6bcf75..921d4efae1dc 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProvider.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProvider.kt @@ -12,18 +12,24 @@ import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.AlbumFileUploadWorker import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.preferences.AppPreferences import com.owncloud.android.R +import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.utils.FilesSyncHelper class UploadListMenuProvider( private val context: Context, private val preferences: AppPreferences, private val userAccountManager: UserAccountManager, private val uploadsStorageManager: UploadsStorageManager, + private val syncedFolderProvider: SyncedFolderProvider, + private val backgroundJobManager: BackgroundJobManager, + private val uploadHelper: () -> FileUploadHelper = { FileUploadHelper.instance() }, private val onGlobalPauseToggled: () -> Unit ) : MenuProvider { @@ -61,12 +67,18 @@ class UploadListMenuProvider( if (paused) { FileUploadWorker.pauseActiveUploads() AlbumFileUploadWorker.pauseActiveUploads() + } else { + FilesSyncHelper.startAutoUploadForEnabledSyncedFolders( + syncedFolderProvider, + backgroundJobManager, + overridePowerSaving = false + ) } - val uploadHelper = FileUploadHelper.instance() + val helper = uploadHelper() userAccountManager.allUsers.filterNotNull().forEach { user -> val ids = uploadsStorageManager.getCurrentUploadIds(user.accountName) - uploadHelper.cancelAndRestartUploadJob(user, ids) + helper.cancelAndRestartUploadJob(user, ids) } onGlobalPauseToggled() } diff --git a/app/src/test/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProviderTest.kt b/app/src/test/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProviderTest.kt new file mode 100644 index 000000000000..ded4c451c65e --- /dev/null +++ b/app/src/test/java/com/owncloud/android/ui/fragment/uploadList/UploadListMenuProviderTest.kt @@ -0,0 +1,63 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.fragment.uploadList + +import android.content.Context +import android.view.MenuItem +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.preferences.AppPreferences +import com.owncloud.android.R +import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.datamodel.UploadsStorageManager +import org.junit.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify + +class UploadListMenuProviderTest { + + private val preferences: AppPreferences = mock { + on { isGlobalUploadPaused() } doReturn true + } + private val backgroundJobManager: BackgroundJobManager = mock() + private val uploadHelper: FileUploadHelper = mock() + private val enabledFolder: SyncedFolder = mock { + on { id } doReturn ENABLED_FOLDER_ID + on { isEnabled } doReturn true + } + private val syncedFolderProvider: SyncedFolderProvider = mock { + on { syncedFolders } doReturn mutableListOf(enabledFolder) + } + private val menuItem: MenuItem = mock { + on { itemId } doReturn R.id.action_toggle_global_pause + } + + private val menuProvider = UploadListMenuProvider( + mock(), + preferences, + mock(), + mock(), + syncedFolderProvider, + backgroundJobManager, + { uploadHelper } + ) {} + + @Test + fun `resuming global pause starts auto upload of enabled folders without waiting for the rescan`() { + menuProvider.onMenuItemSelected(menuItem) + + verify(backgroundJobManager).startAutoUpload(enabledFolder, false) + } + + private companion object { + const val ENABLED_FOLDER_ID = 1L + } +}