Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
7581378
Manually bump library
tobiasKaminsky Jan 7, 2026
c2fddba
chore(deps): update dependency fastlane to v2.230.0
renovate[bot] Jan 8, 2026
db8dfe8
chore(deps): update nextcloud/pr-feedback-action digest to 5227c55
renovate[bot] Jan 8, 2026
aeac903
FolderPickerActivity: show correct folder
tobiasKaminsky Dec 30, 2025
dff5961
fix: battery optimization check
alperozturk96 Nov 28, 2025
abe6d64
fix(calendar): correct ICS event formatting and time zone usage
Gorlesunilkumar Nov 25, 2025
6c1fb0f
🔄 synced local '.github/workflows/' with remote 'config/workflows/'
nextcloud-android-bot Jan 9, 2026
3c8574a
we us kts
tobiasKaminsky Jan 9, 2026
7e772d9
Added function to parallelise uploads. Still need to address non-thre…
k0raph Jan 11, 2026
5011c0f
Fixed notification manager updates jumping around and improved thread…
k0raph Jan 11, 2026
f111082
fixed FileUploadHelper to make sure uploads are correctly reflecting …
k0raph Jan 11, 2026
9789988
removed magic number
k0raph Jan 12, 2026
c3ac6e1
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 12, 2026
57b9744
update updateLibraryHash.sh script to use .kts
tobiasKaminsky Jan 12, 2026
1f23f2e
chore(deps): update dependency fastlane to v2.230.0
renovate[bot] Jan 12, 2026
1979215
Update library to 2026-01-12
tobiasKaminsky Jan 12, 2026
3d8914a
update detectWrongSettings.sh
tobiasKaminsky Jan 12, 2026
846894d
GetCapabilitiesRemoteOperation now returns OCCapability
tobiasKaminsky Sep 3, 2025
185b6f3
Remove logic to handle FAB visibility.
ZetaTom Jan 6, 2026
a280337
fix(theming): Improve color theming and general layouting
AndyScherzinger Jan 8, 2026
a5a9088
style(searchbar): Migrate to M3 search bar style
AndyScherzinger Jan 1, 2026
12321b6
Removed left-over usages of gplay flavor for screenshot tests
PhilLab Jan 1, 2026
6b35ae4
Added documentation for local execution of screenshot tests.
PhilLab Jan 1, 2026
f05c688
androidScreenshotTest now sets up the uiComparison AVD, if not present
PhilLab Jan 1, 2026
8e4d33a
fix(avatar): Fix avatar sizing for non-user avatars
AndyScherzinger Jan 1, 2026
19a4632
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 13, 2026
ebeec51
Declarative UI
tobiasKaminsky Jul 2, 2025
6e13f8a
🔄 synced local '.github/workflows/' with remote 'config/workflows/'
nextcloud-android-bot Jan 13, 2026
20e9e80
revert qa.xml
tobiasKaminsky Jan 13, 2026
5c06c6d
chore(deps): update dependency fastlane to v2.230.0
renovate[bot] Jan 13, 2026
f7427c6
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 14, 2026
0d00742
Onetime apppassword
tobiasKaminsky Oct 9, 2025
78a942c
style(avatar): respect avatar border for TextDrawable
AndyScherzinger Jan 13, 2026
dddd115
fix: auto upload worker running check
alperozturk96 Jan 12, 2026
e77de63
fix
alperozturk96 Jan 13, 2026
8bfd54c
do not show all sync conflict notifications during app launch
alperozturk96 Jan 12, 2026
33c50c1
fix: lint
alperozturk96 Jan 14, 2026
75cbda5
fix: lint
alperozturk96 Jan 14, 2026
61bf250
Show no results page when no results are available.
ZetaTom Jan 14, 2026
8f41347
fix(auto-upload): handle existing sync conflicts
alperozturk96 Jan 12, 2026
5c5fea4
fix(auto-upload): codacy
alperozturk96 Jan 12, 2026
0f54749
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 15, 2026
c68cb19
Minor improvements of testing documentation
PhilLab Aug 24, 2025
27cadc6
Instrumentation tests now automatically grab the notifications permis…
PhilLab Aug 24, 2025
534b7f1
DownloadIT now works for all build variants
PhilLab Oct 28, 2025
df83973
Fixed AbstractIT deleting the wrong account
PhilLab Nov 2, 2025
0c8e14a
AbstractOnServerIT file deletion now ignores non-empty encrypted folders
PhilLab Nov 2, 2025
211600c
Fixed some static warnings in AbstractIT and AbstractOnServerIT
PhilLab Dec 20, 2025
6a97c43
Fixed incorrect lookup of gradle properties file
PhilLab Dec 20, 2025
fc014cf
remove debug login
alperozturk96 Jan 15, 2026
f5de291
Test that move or copy files starts in the file's parent folder
PhilLab Dec 22, 2025
d2dec9b
Removed unnecessary executePendingTransactions()
PhilLab Jan 15, 2026
763e2ef
🔄 synced local '.github/workflows/' with remote 'config/workflows/'
nextcloud-android-bot Jan 15, 2026
19e8bfa
🔄 synced local '.github/workflows/' with remote 'config/workflows/'
nextcloud-android-bot Jan 15, 2026
0fffba7
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 16, 2026
9bd7766
Fixed GalleryAdapter#getFilesCount()
PhilLab Jan 11, 2026
c41049b
Test bugfix of #15918 and #16194: Gallery multiselect could crash
PhilLab Jan 11, 2026
fb3a199
UI test for multi selection in media gallery
PhilLab Jan 2, 2026
be59b91
ThumbnailsCacheManager: fixed mutex and volatile
PhilLab Jan 15, 2026
84d4d64
Added LongLine as linter warning
PhilLab Jan 15, 2026
fcccaf7
Illustrating timing issue with FileActivity#dismissLoadingDialog
PhilLab Dec 15, 2025
84388cd
Fixing timing issue with FileActivity's loading dialog show and dismiss.
PhilLab Dec 16, 2025
13b546a
rework share flow
tobiasKaminsky Dec 19, 2025
89f25e0
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 17, 2026
a82b664
chore(deps): update ubuntu:noble docker digest to 7a39814
renovate[bot] Jan 17, 2026
e48a830
chore(deps): update actions/cache action to v5.0.2
renovate[bot] Jan 17, 2026
6d84c82
extracted various classes, favouring dependency injection to make tes…
k0raph Jan 18, 2026
d13b012
addressed codacy issues
k0raph Jan 19, 2026
3b64542
added a prefer param to allow the user to configure the number of con…
k0raph Jan 18, 2026
2c685f2
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 18, 2026
9af8983
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 19, 2026
d440d99
chore(deps): update ubuntu:noble docker digest to cd1dba6
renovate[bot] Jan 19, 2026
4569ad6
chore(deps): update dependency fastlane to v2.231.0
renovate[bot] Jan 19, 2026
a21ca08
fix(deps): update dependency com.mebigfatguy.fb-contrib:fb-contrib to…
renovate[bot] Jan 19, 2026
620ecaa
Update library to 2026-01-18
tobiasKaminsky Jan 17, 2026
d827009
ran spotless apply
k0raph Jan 20, 2026
cc95502
Explorting alternative solution - parallelising workers - tests added
k0raph Jan 20, 2026
76dc9be
fix(deps): update dependency androidx.compose.foundation:foundation t…
renovate[bot] Jan 19, 2026
cbc919d
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 20, 2026
befe799
fixed FileUploadWorker to correctly update progress of uploading file…
k0raph Jan 20, 2026
3c5c5be
ran spotless apply
k0raph Jan 20, 2026
8110710
removed max concurrent uploads preference. Defaulting to 7 threads
k0raph Jan 23, 2026
85b7833
fixed notifications manager and uploads list. Added relevant unit tests
k0raph Jan 24, 2026
de40047
ran spotless apply
k0raph Jan 24, 2026
7f600ab
ecosystem link handling
alperozturk96 Jan 15, 2026
74b27b8
ecosystem link handling
alperozturk96 Jan 15, 2026
dc072a2
inform user if account not exists
alperozturk96 Jan 16, 2026
cd7a094
inform user if account not exists
alperozturk96 Jan 16, 2026
5b9e43f
update lib
alperozturk96 Jan 16, 2026
a137762
update lib
alperozturk96 Jan 19, 2026
eb947d4
Bump to Gradle 9
tobiasKaminsky Jan 16, 2026
d3e9294
lint
tobiasKaminsky Jan 19, 2026
91a3d4e
todo for gradle.properties
tobiasKaminsky Jan 20, 2026
4a5a606
Gradle 9
tobiasKaminsky Jan 19, 2026
2e8e845
fix build
alperozturk96 Jan 20, 2026
55d896e
fix(navigation): show folder name in SharedListFragment actionbar
ZetaTom Jan 20, 2026
26b0d31
fix(upload-notification): dismiss error notification
alperozturk96 Jan 19, 2026
1b997be
fix(tabs-list-mode): layout switch
alperozturk96 Jan 16, 2026
d3cafef
fix(local-file-list-adapter): navigation
alperozturk96 Jan 19, 2026
74c15ab
chore: java 21
alperozturk96 Jan 21, 2026
36e1ce9
chore: java 21
alperozturk96 Jan 21, 2026
140c433
feat(assistant-screen): PROCESS_TEXT support
alperozturk96 Jan 20, 2026
ed2e505
copy selected text to input bar
alperozturk96 Jan 21, 2026
4314b40
Rename .java to .kt
alperozturk96 Jan 21, 2026
04dde39
feat(settings-activity): m3 switch
alperozturk96 Jan 21, 2026
d0ec004
add license
alperozturk96 Jan 21, 2026
4f9b2d1
style: Fix formatting
AndyScherzinger Jan 21, 2026
e51cc23
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 22, 2026
da7e7e2
fix: auto upload file skip check
alperozturk96 Jan 12, 2026
3a36caf
update tests
alperozturk96 Jan 12, 2026
9e76edd
🔄 synced local '.github/workflows/' with remote 'config/workflows/'
nextcloud-android-bot Jan 22, 2026
a0479fa
handle oc upload construct
alperozturk96 Jan 22, 2026
4313fd2
Apply suggestion from @ZetaTom
alperozturk96 Jan 22, 2026
58462de
Apply suggestion from @ZetaTom
alperozturk96 Jan 22, 2026
5c7f60a
fix(l10n): Update translations from Transifex
nextcloud-bot Jan 23, 2026
6c51f91
fix(auto-upload): sync conflict handling
alperozturk96 Jan 22, 2026
0856f55
fix(auto-upload): sync conflict handling
alperozturk96 Jan 22, 2026
2d60b71
fix(auto-upload): simplify error notification logic
alperozturk96 Jan 22, 2026
413b6ef
fix(auto-upload): simplify error notification logic
alperozturk96 Jan 22, 2026
c8ac9d7
remove public handleLocalBehaviour
alperozturk96 Jan 22, 2026
5f74f9e
remove public handleLocalBehaviour
alperozturk96 Jan 22, 2026
c5971ad
remove public handleLocalBehaviour
alperozturk96 Jan 22, 2026
ede682e
fix codacy
alperozturk96 Jan 22, 2026
77c9265
cleaned up imports
k0raph Jan 24, 2026
afd671c
resolved conflicts from master
k0raph Jan 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkContinuation
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.nextcloud.client.account.User
import com.nextcloud.client.core.Clock
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.extensions.toByteArray
import com.owncloud.android.lib.common.utils.Log_OC
import org.apache.commons.io.FileUtils
Expand All @@ -42,6 +45,7 @@ import org.mockito.kotlin.argThat
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.timeout
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.io.File
Expand All @@ -64,6 +68,7 @@ import java.util.concurrent.TimeoutException
BackgroundJobManagerTest.PeriodicContactsBackup::class,
BackgroundJobManagerTest.ImmediateContactsBackup::class,
BackgroundJobManagerTest.ImmediateContactsImport::class,
BackgroundJobManagerTest.FilesUpload::class,
BackgroundJobManagerTest.Tags::class
)
class BackgroundJobManagerTest {
Expand All @@ -90,6 +95,7 @@ class BackgroundJobManagerTest {
internal lateinit var user: User
internal lateinit var workManager: WorkManager
internal lateinit var clock: Clock
internal lateinit var preferences: AppPreferences
internal lateinit var backgroundJobManager: BackgroundJobManagerImpl
internal lateinit var context: Context

Expand All @@ -100,9 +106,10 @@ class BackgroundJobManagerTest {
whenever(user.accountName).thenReturn(USER_ACCOUNT_NAME)
workManager = mock()
clock = mock()
preferences = mock()
whenever(clock.currentTime).thenReturn(TIMESTAMP)
whenever(clock.currentDate).thenReturn(Date(TIMESTAMP))
backgroundJobManager = BackgroundJobManagerImpl(workManager, clock, mock())
backgroundJobManager = BackgroundJobManagerImpl(workManager, clock, preferences)
}

fun assertHasRequiredTags(tags: Set<String>, jobName: String, user: User? = null) {
Expand Down Expand Up @@ -385,6 +392,63 @@ class BackgroundJobManagerTest {
}
}

class FilesUpload : Fixture() {

@Test
fun start_files_upload_job_enqueues_batches() {
val uploadIds = longArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)

val continuation: WorkContinuation = mock()
whenever(
workManager.beginUniqueWork(any(), any(), any<List<OneTimeWorkRequest>>())
).thenReturn(continuation)

backgroundJobManager.startFilesUploadJob(user, uploadIds, true)

val tagCaptor = argumentCaptor<String>()
val requestsCaptor = argumentCaptor<List<OneTimeWorkRequest>>()

verify(workManager, timeout(1000)).enqueueUniqueWork(
tagCaptor.capture(),
eq(ExistingWorkPolicy.KEEP),
requestsCaptor.capture()
)

val tag = tagCaptor.firstValue
assertTrue(tag.startsWith(BackgroundJobManagerImpl.JOB_FILES_UPLOAD + USER_ACCOUNT_NAME + "_"))

val requests = requestsCaptor.firstValue
assertEquals(6, requests.size)

// Check first batch [1, 2]
val data1 = requests[0].workSpec.input
assertEquals(true, data1.getBoolean(FileUploadWorker.SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION, false))
assertEquals(USER_ACCOUNT_NAME, data1.getString(FileUploadWorker.ACCOUNT))
assertEquals(2, data1.getInt(FileUploadWorker.TOTAL_UPLOAD_SIZE, 0))
assertTrue(longArrayOf(1, 2).contentEquals(data1.getLongArray(FileUploadWorker.UPLOAD_IDS)!!))
assertEquals(0, data1.getInt(FileUploadWorker.CURRENT_BATCH_INDEX, -1))

// Check second batch [3, 4]
val data2 = requests[1].workSpec.input
assertTrue(longArrayOf(3, 4).contentEquals(data2.getLongArray(FileUploadWorker.UPLOAD_IDS)!!))
assertEquals(1, data2.getInt(FileUploadWorker.CURRENT_BATCH_INDEX, -1))

// Check third batch [5]
val data3 = requests[2].workSpec.input
assertTrue(longArrayOf(5, 6).contentEquals(data3.getLongArray(FileUploadWorker.UPLOAD_IDS)!!))
assertEquals(2, data3.getInt(FileUploadWorker.CURRENT_BATCH_INDEX, -1))
}

@Test
fun start_files_upload_job_does_nothing_when_empty() {
val uploadIds = longArrayOf()

backgroundJobManager.startFilesUploadJob(user, uploadIds, true)

verify(workManager, timeout(1000).times(0)).beginUniqueWork(any(), any(), any<List<OneTimeWorkRequest>>())
}
}

class Tags {
@Test
fun split_tag_key_and_value() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.nextcloud.client.jobs.metadata.MetadataWorker
import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker
import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.jobs.upload.UploadNotificationManager
import com.nextcloud.client.logger.Logger
import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.client.preferences.AppPreferences
Expand All @@ -39,6 +40,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject
import javax.inject.Provider
import kotlin.random.Random

/**
* This factory is responsible for creating all background jobs and for injecting worker dependencies.
Expand Down Expand Up @@ -238,6 +240,7 @@ class BackgroundJobFactory @Inject constructor(
backgroundJobManager.get(),
preferences,
context,
UploadNotificationManager(context, viewThemeUtils.get(), Random.nextInt()),
params
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,48 +657,41 @@ internal class BackgroundJobManagerImpl(
*/
override fun startFilesUploadJob(user: User, uploadIds: LongArray, showSameFileAlreadyExistsNotification: Boolean) {
defaultDispatcherScope.launch {
val batchSize = FileUploadHelper.MAX_FILE_COUNT
val batches = uploadIds.toList().chunked(batchSize)
val tag = startFileUploadJobTag(user.accountName)
val chunkSize = (uploadIds.size / 5).coerceAtLeast(1)
val batches = uploadIds.toList().chunked(chunkSize)
val executionId = System.currentTimeMillis()
val tag = "${startFileUploadJobTag(user.accountName)}_$executionId"

val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()

val dataBuilder = Data.Builder()
.putBoolean(
FileUploadWorker.SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION,
showSameFileAlreadyExistsNotification
)
.putString(FileUploadWorker.ACCOUNT, user.accountName)
.putInt(FileUploadWorker.TOTAL_UPLOAD_SIZE, uploadIds.size)

val workRequests = batches.mapIndexed { index, batch ->
dataBuilder
val data = Data.Builder()
.putBoolean(
FileUploadWorker.SHOW_SAME_FILE_ALREADY_EXISTS_NOTIFICATION,
showSameFileAlreadyExistsNotification
)
.putString(FileUploadWorker.ACCOUNT, user.accountName)
.putInt(FileUploadWorker.TOTAL_UPLOAD_SIZE, chunkSize)
.putLongArray(FileUploadWorker.UPLOAD_IDS, batch.toLongArray())
.putInt(FileUploadWorker.CURRENT_BATCH_INDEX, index)
.build()

oneTimeRequestBuilder(FileUploadWorker::class, JOB_FILES_UPLOAD, user)
.addTag(tag)
.setInputData(dataBuilder.build())
.setInputData(data)
.setConstraints(constraints)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
}

// Chain the work requests sequentially
if (workRequests.isNotEmpty()) {
var workChain = workManager.beginUniqueWork(
workManager.enqueueUniqueWork(
tag,
ExistingWorkPolicy.APPEND_OR_REPLACE,
workRequests.first()
ExistingWorkPolicy.KEEP,
workRequests
)

workRequests.drop(1).forEach { request ->
workChain = workChain.then(request)
}

workChain.enqueue()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import com.nextcloud.client.device.PowerManagementService
import com.nextcloud.client.jobs.BackgroundJobManager
import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.currentUploadFileOperation
import com.nextcloud.client.notifications.AppWideNotificationManager
import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.activeUploadFileOperations
import com.nextcloud.client.network.Connectivity
import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.client.notifications.AppWideNotificationManager
import com.nextcloud.utils.extensions.getUploadIds
import com.owncloud.android.MainApp
import com.owncloud.android.R
Expand Down Expand Up @@ -372,17 +374,14 @@ class FileUploadHelper {

@Suppress("ReturnCount")
fun isUploadingNow(upload: OCUpload?): Boolean {
val currentUploadFileOperation = currentUploadFileOperation
if (currentUploadFileOperation == null || currentUploadFileOperation.user == null) return false
if (upload == null || upload.accountName != currentUploadFileOperation.user.accountName) return false

return if (currentUploadFileOperation.oldFile != null) {
// For file conflicts check old file remote path
upload.remotePath == currentUploadFileOperation.remotePath ||
upload.remotePath == currentUploadFileOperation.oldFile!!
.remotePath
} else {
upload.remotePath == currentUploadFileOperation.remotePath
upload ?: return false

return activeUploadFileOperations.values.any { operation ->
operation.user?.accountName == upload.accountName &&
(
upload.remotePath == operation.remotePath ||
upload.remotePath == operation.oldFile?.remotePath
)
}
}

Expand Down
Loading