Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions app/src/main/java/com/nextcloud/client/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import android.content.pm.PackageManager
import android.content.res.Resources
import android.media.AudioManager
import android.os.Handler
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.nextcloud.client.account.CurrentAccountProvider
import com.nextcloud.client.account.UserAccountManager
Expand All @@ -27,6 +28,7 @@ import com.nextcloud.client.core.ClockImpl
import com.nextcloud.client.core.ThreadPoolAsyncRunner
import com.nextcloud.client.database.dao.ArbitraryDataDao
import com.nextcloud.client.device.DeviceInfo
import com.nextcloud.client.jobs.BackgroundJobManager
import com.nextcloud.client.jobs.operation.FileOperationHelper
import com.nextcloud.client.logger.FileLogHandler
import com.nextcloud.client.logger.Logger
Expand Down Expand Up @@ -61,6 +63,8 @@ import com.owncloud.android.ui.activities.data.activities.RemoteActivitiesReposi
import com.owncloud.android.ui.activities.data.files.FilesRepository
import com.owncloud.android.ui.activities.data.files.FilesServiceApiImpl
import com.owncloud.android.ui.activities.data.files.RemoteFilesRepository
import com.owncloud.android.ui.activity.filedisplayactivity.FileDisplayActivityRepository
import com.owncloud.android.ui.activity.filedisplayactivity.FileDisplayActivityRepositoryImpl
import com.owncloud.android.ui.dialog.setupEncryption.CertificateValidator
import com.owncloud.android.utils.theme.ViewThemeUtils
import dagger.Module
Expand All @@ -70,6 +74,7 @@ import java.io.File
import javax.inject.Named
import javax.inject.Provider
import javax.inject.Singleton
import com.owncloud.android.ui.fragment.filesRepository.RemoteFilesRepository as OCFileListFilesRepository

@Module(includes = [ComponentsModule::class, VariantComponentsModule::class, BuildTypeComponentsModule::class])
@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -129,6 +134,20 @@ internal class AppModule {
context: Context
): FileDataStorageManager = FileDataStorageManager(currentAccountProvider.user, context.contentResolver)

@Provides
fun fileDisplayActivityRepository(
context: Context,
clientRepository: ClientRepository,
storageManager: FileDataStorageManager,
backgroundJobManager: BackgroundJobManager
): FileDisplayActivityRepository = FileDisplayActivityRepositoryImpl(
context,
clientRepository,
OCFileListFilesRepository(clientRepository, ProcessLifecycleOwner.get()),
storageManager,
backgroundJobManager
)

@Provides
fun currentAccountProvider(accountManager: UserAccountManager): CurrentAccountProvider = accountManager

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ import androidx.core.view.MenuItemCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
Expand All @@ -68,18 +70,15 @@ import com.nextcloud.client.di.ViewModelFactory
import com.nextcloud.client.editimage.EditImageActivity
import com.nextcloud.client.files.DeepLinkHandler
import com.nextcloud.client.jobs.download.FileDownloadEventBroadcaster
import com.nextcloud.client.jobs.download.FileDownloadHelper
import com.nextcloud.client.jobs.download.FileDownloadWorker
import com.nextcloud.client.jobs.folderDownload.FolderDownloadEventBroadcaster
import com.nextcloud.client.jobs.upload.FileUploadEventBroadcaster
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.network.ClientFactory.CreationException
import com.nextcloud.client.player.model.file.toPlaybackCollection
import com.nextcloud.client.player.ui.audio.AudioPlayerLauncher
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.client.utils.IntentUtil
import com.nextcloud.model.OCUploadLocalPathData
import com.nextcloud.ui.composeActivity.ComposeProcessTextAlias
import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.extensions.getParcelableArgument
Expand Down Expand Up @@ -112,7 +111,6 @@ import com.owncloud.android.lib.resources.files.SearchRemoteOperation
import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation
import com.owncloud.android.operations.CopyFileOperation
import com.owncloud.android.operations.CreateFolderOperation
import com.owncloud.android.operations.DownloadType
import com.owncloud.android.operations.FolderRefreshScheduler
import com.owncloud.android.operations.MoveFileOperation
import com.owncloud.android.operations.RefreshFolderOperation
Expand Down Expand Up @@ -329,7 +327,7 @@ class FileDisplayActivity :
}

checkStoragePath()
observeWorkerState()
observeViewModel()
handleBackPress()
setupDrawer(menuItemId)
}
Expand Down Expand Up @@ -1177,15 +1175,7 @@ class FileDisplayActivity :
return@isNetworkAndServerAvailable
}

val data = OCUploadLocalPathData.forFile(
user.orElseThrow(Supplier { RuntimeException() }),
filePaths,
decryptedRemotePaths,
behaviour,
createRemoteFolder = true
)

FileUploadHelper.instance().uploadNewFiles(data)
viewModel.uploadFiles(filePaths, decryptedRemotePaths, behaviour)
}
} else {
lifecycleScope.launch(Dispatchers.IO) {
Expand Down Expand Up @@ -2105,12 +2095,26 @@ class FileDisplayActivity :

override fun isDrawerIndicatorAvailable(): Boolean = isRoot(getCurrentDir())

private fun observeWorkerState() {
private fun observeViewModel() {
lifecycleScope.launch {
viewModel.observeOfflineWorker(onComplete = {
refreshCurrentDirectory()
})
}

lifecycleScope.launch {
viewModel.syncFolderResult.collect {
onSyncFinished()
}
Comment on lines +2105 to +2108
}

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.recommendedFiles.collect { files ->
listOfFilesFragment?.adapter?.updateRecommendedFiles(files)
}
}
}
}

fun canPreviewInMediaPager(file: OCFile?): Boolean =
Expand Down Expand Up @@ -2331,7 +2335,7 @@ class FileDisplayActivity :
}

supportInvalidateOptionsMenu()
fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir)
viewModel.fetchRecommendedFiles(ignoreETag = true, currentDir)
}

override fun onAutoUploadFolderRemoved(
Expand Down Expand Up @@ -2503,7 +2507,7 @@ class FileDisplayActivity :
}

refreshGalleryFragmentIfNeeded()
fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir)
viewModel.fetchRecommendedFiles(ignoreETag = true, currentDir)
}

private fun onRenameFileOperationFinishForFileFragment(fragment: FileFragment, ocFile: OCFile, user: User) {
Expand Down Expand Up @@ -2599,9 +2603,8 @@ class FileDisplayActivity :
}

private fun requestForDownload() {
val user = user.orElseThrow(Supplier { RuntimeException() })
mWaitingToPreview?.let {
FileDownloadHelper.instance().downloadFileIfNotStartedBefore(user, it)
viewModel.downloadFileIfNotStartedBefore(it)
}
}

Expand Down Expand Up @@ -2661,64 +2664,19 @@ class FileDisplayActivity :
private fun executeSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean) {
val folder = folder ?: return

user.ifPresent { user ->
syncState = EmptyListState.LOADING

RefreshFolderOperation(
folder,
System.currentTimeMillis(),
false,
ignoreETag,
storageManager,
user,
applicationContext
).execute(
account,
this,
{ _, _ -> onSyncFinished() },
handler,
null
)

fetchRecommendedFilesIfNeeded(ignoreETag, folder)
}
}

private fun fetchRecommendedFilesIfNeeded(ignoreETag: Boolean, folder: OCFile?) {
val optionalCapabilities = capabilities
if (optionalCapabilities.isEmpty) {
if (user.isEmpty) {
return
}

if (folder?.isRootDirectory == false || optionalCapabilities.get().recommendations.isFalse) {
return
}
syncState = EmptyListState.LOADING

user.ifPresent { user ->
val accountName = user.accountName
val fragment = this.listOfFilesFragment
lifecycleScope.launch(Dispatchers.IO) {
val recommendedFiles = filesRepository.fetchRecommendedFiles(accountName, ignoreETag, storageManager)
withContext(Dispatchers.Main) {
fragment?.adapter?.updateRecommendedFiles(recommendedFiles)
}
}
}
viewModel.syncFolder(folder, ignoreETag)

viewModel.fetchRecommendedFiles(ignoreETag, folder)
}

private fun requestForDownload(file: OCFile, downloadBehaviour: String, packageName: String, activityName: String) {
val currentUser = user.orElseThrow(Supplier { RuntimeException() })
if (!FileDownloadHelper.instance().isDownloading(currentUser, file)) {
FileDownloadHelper.instance().downloadFile(
currentUser,
file,
downloadBehaviour,
DownloadType.DOWNLOAD,
activityName,
packageName,
null
)
}
viewModel.downloadFile(file, downloadBehaviour, packageName, activityName)
}

private fun sendDownloadedFile(packageName: String, activityName: String) {
Expand Down Expand Up @@ -3344,12 +3302,12 @@ class FileDisplayActivity :

// region MetadataSyncJob
private fun startMetadataSyncForRoot() {
backgroundJobManager.startMetadataSyncJob(OCFile.ROOT_PATH, folderAlreadySynced = true)
viewModel.startMetadataSync(OCFile.ROOT_PATH, folderAlreadySynced = true)
}

private fun startMetadataSyncForCurrentDir() {
val currentDirId = file?.decryptedRemotePath ?: return
backgroundJobManager.startMetadataSyncJob(currentDirId)
viewModel.startMetadataSync(currentDirId, folderAlreadySynced = false)
}
// endregion

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.activity.filedisplayactivity

import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.common.operations.RemoteOperationResult

interface FileDisplayActivityRepository {
suspend fun fetchRecommendedFiles(ignoreETag: Boolean, folder: OCFile?): ArrayList<OCFile>?

suspend fun syncFolder(folder: OCFile, ignoreETag: Boolean): RemoteOperationResult<*>

fun downloadFileIfNotStartedBefore(file: OCFile)

fun downloadFile(file: OCFile, downloadBehaviour: String, packageName: String, activityName: String)

fun uploadFiles(localPaths: Array<String>, remotePaths: Array<String>, localBehaviour: Int)

fun startMetadataSync(remotePath: String, folderAlreadySynced: Boolean)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.activity.filedisplayactivity

import android.content.Context
import com.nextcloud.client.jobs.BackgroundJobManager
import com.nextcloud.client.jobs.download.FileDownloadHelper
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.model.OCUploadLocalPathData
import com.nextcloud.repository.ClientRepository
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.operations.DownloadType
import com.owncloud.android.operations.RefreshFolderOperation
import com.owncloud.android.ui.fragment.filesRepository.FilesRepository
import com.owncloud.android.utils.theme.CapabilityUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class FileDisplayActivityRepositoryImpl(
private val context: Context,
private val clientRepository: ClientRepository,
private val filesRepository: FilesRepository,
private val storageManager: FileDataStorageManager,
private val backgroundJobManager: BackgroundJobManager
) : FileDisplayActivityRepository {

private val user
get() = storageManager.user
Comment on lines +34 to +35

private val capabilities
get() = CapabilityUtils.getCapability(user, context)

override suspend fun fetchRecommendedFiles(ignoreETag: Boolean, folder: OCFile?): ArrayList<OCFile>? {
if (folder?.isRootDirectory == false || capabilities.recommendations.isFalse) {
return null
}

return filesRepository.fetchRecommendedFiles(user.accountName, ignoreETag, storageManager)
}

override suspend fun syncFolder(folder: OCFile, ignoreETag: Boolean): RemoteOperationResult<*> {
val client = clientRepository.getOwncloudClient()
?: return RemoteOperationResult<RemoteOperationResult.ResultCode>(
RemoteOperationResult.ResultCode.UNKNOWN_ERROR
)

return withContext(Dispatchers.IO) {
RefreshFolderOperation(
folder,
System.currentTimeMillis(),
false,
ignoreETag,
storageManager,
user,
context
).execute(client)
}
}

override fun downloadFileIfNotStartedBefore(file: OCFile) {
FileDownloadHelper.instance().downloadFileIfNotStartedBefore(user, file)
}

override fun downloadFile(file: OCFile, downloadBehaviour: String, packageName: String, activityName: String) {
val downloadHelper = FileDownloadHelper.instance()
if (downloadHelper.isDownloading(user, file)) {
return
}

downloadHelper.downloadFile(
user,
file,
downloadBehaviour,
DownloadType.DOWNLOAD,
activityName,
packageName,
null
)
}

override fun uploadFiles(localPaths: Array<String>, remotePaths: Array<String>, localBehaviour: Int) {
val data = OCUploadLocalPathData.forFile(
user,
localPaths,
remotePaths,
localBehaviour,
createRemoteFolder = true
)
FileUploadHelper.instance().uploadNewFiles(data)
}

override fun startMetadataSync(remotePath: String, folderAlreadySynced: Boolean) {
backgroundJobManager.startMetadataSyncJob(remotePath, folderAlreadySynced)
}
}
Loading
Loading