Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c4ac182
Albums functionality.
surinder-tsys Jun 2, 2025
a2e56ed
Rename .java to .kt
surinder-tsys Jun 6, 2025
e905d04
Code refactoring and operation migrated to kotlin
surinder-tsys Jun 6, 2025
27dcc84
Fix -> video click event, overflow menu item hide and theming add menu
surinder-tsys Jun 6, 2025
4f8d655
Fix: preview album items
surinder-tsys Jul 11, 2025
ecde7ba
upload files to album, hide video overlay icon, minor ui and bug fixes
surinder-tsys Jan 22, 2026
1328930
wip
tobiasKaminsky Jan 23, 2026
792e0d7
Albums functionality.
surinder-tsys Jun 2, 2025
8bab3eb
Rename .java to .kt
surinder-tsys Jun 6, 2025
3fe7872
Code refactoring and operation migrated to kotlin
surinder-tsys Jun 6, 2025
101394d
upload files to album, hide video overlay icon, minor ui and bug fixes
surinder-tsys Jan 22, 2026
324776a
small fix
tobiasKaminsky Jan 26, 2026
edd9070
code fixes
surinder-tsys Jan 29, 2026
3a2ae9e
album upload worker state handled via localbroadcastmanager
surinder-tsys Jan 30, 2026
90fbbed
simplify upload file calls
alperozturk96 Feb 2, 2026
8a461b1
separate worker parameters
alperozturk96 Feb 2, 2026
e17dd8f
separate params
alperozturk96 Feb 2, 2026
d2fe29f
simplify album fragment
alperozturk96 Feb 2, 2026
9602784
use optional generic function to access fragment
alperozturk96 Feb 3, 2026
094e1d3
fix code analytics, simplify
alperozturk96 Feb 3, 2026
c7488d4
m3 card
alperozturk96 Feb 19, 2026
e4daa1e
m3 card
alperozturk96 Feb 19, 2026
fc8dfd7
use fab
alperozturk96 Feb 19, 2026
bd7fbf0
use action bottom sheet for album item actions
alperozturk96 Feb 19, 2026
0b44b02
use action bottom sheet for album item actions
alperozturk96 Feb 19, 2026
e82e163
fix fab button appereance
alperozturk96 Feb 20, 2026
9ba6887
fix git conflict
alperozturk96 Feb 26, 2026
a793bf6
small typo fixes
alperozturk96 Feb 26, 2026
1fbd193
use outlined card
alperozturk96 Feb 26, 2026
3837063
album items fragment code cleanup
alperozturk96 Feb 26, 2026
053840b
fix(spotbugs): NAB: Needless Autoboxing
alperozturk96 Feb 26, 2026
8df9174
fix menu item highlighting
alperozturk96 Feb 26, 2026
af3d991
adopt overlay manager
alperozturk96 Mar 18, 2026
02cfad8
add albumOperationListener
alperozturk96 Mar 26, 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,6 +16,7 @@ import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.network.Connectivity
import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.model.OCUploadLocalPathData
import com.owncloud.android.AbstractOnServerIT
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager
Expand Down Expand Up @@ -123,7 +124,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
fun testKeepLocalAndOverwriteRemoteStatic() {
val file = getDummyFile("chunkedFile.txt")

FileUploadHelper().uploadNewFiles(
val data = OCUploadLocalPathData(
user,
arrayOf(file.absolutePath),
arrayOf("/testFile.txt"),
Expand All @@ -135,6 +136,8 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
NameCollisionPolicy.DEFAULT
)

FileUploadHelper().uploadNewFiles(data)

longSleep()

val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
Expand Down Expand Up @@ -239,7 +242,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
fun testKeepBothStatic() {
val file = getDummyFile("nonEmpty.txt")

FileUploadHelper().uploadNewFiles(
val data = OCUploadLocalPathData(
user,
arrayOf(file.absolutePath),
arrayOf("/testFile.txt"),
Expand All @@ -250,6 +253,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
false,
NameCollisionPolicy.DEFAULT
)
FileUploadHelper().uploadNewFiles(data)

longSleep()

Expand Down Expand Up @@ -347,7 +351,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
fun testKeepServerStatic() {
val file = getDummyFile("chunkedFile.txt")

FileUploadHelper().uploadNewFiles(
val data = OCUploadLocalPathData(
user,
arrayOf(file.absolutePath),
arrayOf("/testFile.txt"),
Expand All @@ -358,6 +362,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
false,
NameCollisionPolicy.DEFAULT
)
FileUploadHelper().uploadNewFiles(data)

longSleep()

Expand Down Expand Up @@ -451,7 +456,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
fun testKeepCancelStatic() {
val file = getDummyFile("chunkedFile.txt")

FileUploadHelper().uploadNewFiles(
val data = OCUploadLocalPathData(
user,
arrayOf(file.absolutePath),
arrayOf("/testFile.txt"),
Expand All @@ -462,6 +467,7 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
false,
NameCollisionPolicy.DEFAULT
)
FileUploadHelper().uploadNewFiles(data)

longSleep()

Expand Down
5 changes: 4 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
~ SPDX-FileCopyrightText: 2024-2025 TSI-mc <surinder.kumar@t-systems.com>
~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
-->
Expand Down Expand Up @@ -637,6 +637,9 @@
android:launchMode="singleTop"
android:theme="@style/Theme.ownCloud.Dialog.NoTitle"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.activity.AlbumsPickerActivity"
android:exported="false" />
<activity
android:name=".ui.activity.ShareActivity"
android:exported="false"
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.nextcloud.ui.ImageDetailFragment;
import com.nextcloud.ui.SetOnlineStatusBottomSheet;
import com.nextcloud.ui.SetStatusMessageBottomSheet;
import com.nextcloud.ui.albumItemActions.AlbumItemActionsBottomSheet;
import com.nextcloud.ui.composeActivity.ComposeActivity;
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
Expand All @@ -47,6 +48,7 @@
import com.owncloud.android.services.AccountManagerService;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.syncadapter.FileSyncService;
import com.owncloud.android.ui.activity.AlbumsPickerActivity;
import com.owncloud.android.ui.activity.BaseActivity;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
Expand Down Expand Up @@ -81,6 +83,7 @@
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
import com.owncloud.android.ui.dialog.CreateAlbumDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
Expand Down Expand Up @@ -115,6 +118,8 @@
import com.owncloud.android.ui.fragment.OCFileListFragment;
import com.owncloud.android.ui.fragment.SharedListFragment;
import com.owncloud.android.ui.fragment.UnifiedSearchFragment;
import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment;
import com.owncloud.android.ui.fragment.albums.AlbumsFragment;
import com.owncloud.android.ui.fragment.community.CommunityFragment;
import com.owncloud.android.ui.fragment.contactsbackup.BackupFragment;
import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment;
Expand Down Expand Up @@ -516,4 +521,19 @@ abstract class ComponentsModule {

@ContributesAndroidInjector
abstract CommunityFragment communityFragment();

@ContributesAndroidInjector
abstract AlbumsPickerActivity albumsPickerActivity();

@ContributesAndroidInjector
abstract CreateAlbumDialogFragment createAlbumDialogFragment();

@ContributesAndroidInjector
abstract AlbumsFragment albumsFragment();

@ContributesAndroidInjector
abstract AlbumItemsFragment albumItemsFragment();

@ContributesAndroidInjector
abstract AlbumItemActionsBottomSheet albumItemActionsBottomSheet();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.nextcloud.client.jobs.BackgroundJobManager
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.logger.Logger
import com.nextcloud.model.OCUploadLocalPathData
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.files.services.NameCollisionPolicy
import com.owncloud.android.operations.UploadFileOperation
Expand Down Expand Up @@ -166,17 +167,8 @@ class DocumentScanViewModel @Inject constructor(
uploadFolder + OCFile.PATH_SEPARATOR + File(it).name
}.toTypedArray()

FileUploadHelper.instance().uploadNewFiles(
currentAccountProvider.user,
pageList.toTypedArray(),
uploadPaths,
FileUploadWorker.LOCAL_BEHAVIOUR_DELETE,
true,
UploadFileOperation.CREATED_BY_USER,
false,
false,
NameCollisionPolicy.ASK_USER
)
val data = OCUploadLocalPathData.forDocument(currentAccountProvider.user, pageList.toTypedArray(), uploadPaths)
FileUploadHelper.instance().uploadNewFiles(data)
}

fun onExportCanceled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.logger.Logger
import com.nextcloud.model.OCUploadLocalPathData
import com.owncloud.android.R
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.files.services.NameCollisionPolicy
Expand Down Expand Up @@ -108,18 +109,8 @@ class GeneratePdfFromImagesWork(
private fun uploadFile(user: User, uploadFolder: String, pdfPath: String) {
val uploadPath = uploadFolder + OCFile.PATH_SEPARATOR + File(pdfPath).name

FileUploadHelper().uploadNewFiles(
user,
arrayOf(pdfPath),
arrayOf(uploadPath),
// MIME type will be detected from file name
FileUploadWorker.LOCAL_BEHAVIOUR_DELETE,
true,
UploadFileOperation.CREATED_BY_USER,
false,
false,
NameCollisionPolicy.ASK_USER
)
val data = OCUploadLocalPathData.forDocument(user, arrayOf(pdfPath), arrayOf(uploadPath))
FileUploadHelper().uploadNewFiles(data)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.canhub.cropper.CropImageView
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.model.OCUploadLocalPathData
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityEditImageBinding
Expand Down Expand Up @@ -95,17 +96,18 @@ class EditImageActivity :
resultUri?.substring(resultUri.lastIndexOf('.'))

resultUri?.let {
FileUploadHelper().uploadNewFiles(
val data = OCUploadLocalPathData(
user = storageManager.user,
localPaths = arrayOf(it),
remotePaths = arrayOf(file.parentRemotePath + File.separator + newFileName),
createRemoteFolder = false,
createdBy = UploadFileOperation.CREATED_BY_USER,
creationType = UploadFileOperation.CREATED_BY_USER,
requiresWifi = false,
requiresCharging = false,
nameCollisionPolicy = NameCollisionPolicy.RENAME,
collisionPolicy = NameCollisionPolicy.RENAME,
localBehavior = FileUploadWorker.LOCAL_BEHAVIOUR_DELETE
)
FileUploadHelper().uploadNewFiles(data)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2026 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.nextcloud.client.jobs
Expand Down Expand Up @@ -29,6 +30,7 @@ import com.nextcloud.client.jobs.download.FileDownloadWorker
import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker
import com.nextcloud.client.jobs.metadata.MetadataWorker
import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker
import com.nextcloud.client.jobs.upload.AlbumFileUploadWorker
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.logger.Logger
import com.nextcloud.client.network.ConnectivityService
Expand Down Expand Up @@ -97,6 +99,7 @@ class BackgroundJobFactory @Inject constructor(
CalendarImportWork::class -> createCalendarImportWork(context, workerParameters)
FilesExportWork::class -> createFilesExportWork(context, workerParameters)
FileUploadWorker::class -> createFilesUploadWorker(context, workerParameters)
AlbumFileUploadWorker::class -> createAlbumsFilesUploadWorker(context, workerParameters)
FileDownloadWorker::class -> createFilesDownloadWorker(context, workerParameters)
GeneratePdfFromImagesWork::class -> createPDFGenerateWork(context, workerParameters)
HealthStatusWork::class -> createHealthStatusWork(context, workerParameters)
Expand Down Expand Up @@ -259,6 +262,20 @@ class BackgroundJobFactory @Inject constructor(
params
)

private fun createAlbumsFilesUploadWorker(context: Context, params: WorkerParameters): AlbumFileUploadWorker =
AlbumFileUploadWorker(
uploadsStorageManager,
connectivityService,
powerManagementService,
accountManager,
viewThemeUtils.get(),
localBroadcastManager.get(),
backgroundJobManager.get(),
preferences,
context,
params
)

private fun createPDFGenerateWork(context: Context, params: WorkerParameters): GeneratePdfFromImagesWork =
GeneratePdfFromImagesWork(
appContext = context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2026 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.nextcloud.client.jobs
Expand Down Expand Up @@ -132,6 +133,7 @@ interface BackgroundJobManager {
fun startNotificationJob(subject: String, signature: String)
fun startAccountRemovalJob(accountName: String, remoteWipe: Boolean)
fun startFilesUploadJob(user: User, uploadIds: LongArray, showSameFileAlreadyExistsNotification: Boolean)
fun startAlbumFilesUploadJob(user: User, uploadIds: LongArray, albumName: String)
fun getFileUploads(user: User): LiveData<List<JobInfo>>
fun cancelFilesUploadJob(user: User)
fun isStartFileUploadJobScheduled(accountName: String): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.nextcloud.client.jobs.download.FileDownloadWorker
import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker
import com.nextcloud.client.jobs.metadata.MetadataWorker
import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker
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.jobs.worker.WorkerFilesPayload
Expand Down Expand Up @@ -117,6 +118,7 @@ internal class BackgroundJobManagerImpl(
const val OFFLINE_OPERATIONS_PERIODIC_JOB_INTERVAL_MINUTES = 5L
const val DEFAULT_IMMEDIATE_JOB_DELAY_SEC = 3L
const val DEFAULT_BACKOFF_CRITERIA_DELAY_SEC = 300L
const val ALBUM_JOB_FILES_UPLOAD = "album_files_upload"

private const val KEEP_LOG_MILLIS = 1000 * 60 * 60 * 24 * 3L

Expand Down Expand Up @@ -669,6 +671,61 @@ internal class BackgroundJobManagerImpl(
}
}

private fun startAlbumsFileUploadJobTag(accountName: String): String = ALBUM_JOB_FILES_UPLOAD + accountName

/**
* This method supports uploading and copying selected files to Album
*
* @param user The user for whom the upload job is being created.
* @param uploadIds Array of upload IDs to be processed. These IDs originate from multiple sources
* and cannot be determined directly from the account name or a single function
* within the worker.
* @param albumName Album on which selected files should be copy after upload
*/
override fun startAlbumFilesUploadJob(user: User, uploadIds: LongArray, albumName: String) {
defaultDispatcherScope.launch {
val batchSize = FileUploadHelper.MAX_FILE_COUNT
val batches = uploadIds.toList().chunked(batchSize)
val tag = startAlbumsFileUploadJobTag(user.accountName)

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

val dataBuilder = Data.Builder()
.putString(AlbumFileUploadWorker.ACCOUNT, user.accountName)
.putInt(AlbumFileUploadWorker.TOTAL_UPLOAD_SIZE, uploadIds.size)
.putString(AlbumFileUploadWorker.ALBUM_NAME, albumName)

val workRequests = batches.mapIndexed { index, batch ->
dataBuilder
.putLongArray(AlbumFileUploadWorker.UPLOAD_IDS, batch.toLongArray())
.putInt(AlbumFileUploadWorker.CURRENT_BATCH_INDEX, index)

oneTimeRequestBuilder(AlbumFileUploadWorker::class, ALBUM_JOB_FILES_UPLOAD, user)
.addTag(tag)
.setInputData(dataBuilder.build())
.setConstraints(constraints)
.build()
}

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

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

workChain.enqueue()
}
}
}

private fun startFileDownloadJobTag(accountName: String, fileId: Long): String =
JOB_FOLDER_DOWNLOAD + accountName + fileId

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,15 @@ open class WorkerNotificationManager(
notification,
ForegroundServiceType.DataSync
)

fun createSilentNotification(title: String, iconId: Int): Notification = notificationBuilder
.setContentTitle(title)
.setSmallIcon(iconId)
.setOngoing(true)
.setSound(null)
.setVibrate(null)
.setOnlyAlertOnce(true)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setSilent(true)
.build()
}
Loading
Loading