Skip to content
Merged
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
33 changes: 20 additions & 13 deletions app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,27 @@ interface FileDao {

@Query(
"""
SELECT *
FROM filelist
WHERE parent = :parentId
AND file_owner = :accountName
AND (content_type != :dirType AND content_type != :webdavType)
ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}
"""
SELECT
EXISTS (
SELECT 1
FROM filelist
WHERE parent = :parentId
AND file_owner = :accountName
AND content_type != '${MimeType.DIRECTORY}'
AND content_type != '${MimeType.WEBDAV_FOLDER}'
)
AND NOT EXISTS (
SELECT 1
FROM filelist
WHERE parent = :parentId
AND file_owner = :accountName
AND content_type != '${MimeType.DIRECTORY}'
AND content_type != '${MimeType.WEBDAV_FOLDER}'
AND media_path IS NULL
)
"""
)
suspend fun getSubfiles(
parentId: Long,
accountName: String,
dirType: String = MimeType.DIRECTORY,
webdavType: String = MimeType.WEBDAV_FOLDER
): List<FileEntity>
fun isFolderFullyDownloaded(parentId: Long, accountName: String): Boolean
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least one child is exists as file and no child's media_path is null and not directory.


@Query(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ fun FileDataStorageManager.getDecryptedPath(file: OCFile): String {
.joinToString(OCFile.PATH_SEPARATOR)
}

suspend fun FileDataStorageManager.getSubfiles(id: Long, accountName: String): List<OCFile> =
fileDao.getSubfiles(id, accountName).map {
createFileInstance(it)
}

fun FileDataStorageManager.getNonEncryptedSubfolders(id: Long, accountName: String): List<OCFile> =
fileDao.getNonEncryptedSubfolders(id, accountName).map {
createFileInstance(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,6 @@ import com.owncloud.android.datamodel.OCFileDepth.FirstLevel
import com.owncloud.android.datamodel.OCFileDepth.Root
import com.owncloud.android.utils.FileStorageUtils

@Suppress("ReturnCount")
fun List<OCFile>.hasSameContentAs(other: List<OCFile>): Boolean {
if (this.size != other.size) return false

if (this === other) return true

for (i in this.indices) {
val a = this[i]
val b = other[i]

if (a != b) return false
if (a.fileId != b.fileId) return false
if (a.etag != b.etag) return false
if (a.modificationTimestamp != b.modificationTimestamp) return false

if (a.fileLength != b.fileLength) return false
if (a.isFavorite != b.isFavorite) return false

if (a.fileName != b.fileName) return false
}

return true
}

fun List<OCFile>.filterFilenames(): List<OCFile> = distinctBy { it.fileName }

fun OCFile.isTempFile(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1735,48 +1735,74 @@ class FileDisplayActivity :
}
}

private inner class FileDownloadStartedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log_OC.d(TAG, "download worker started")
handleDownloadWorkerState()
private enum class FileDownloadIndicator(val iconId: Int) {
Downloading(R.drawable.ic_synchronizing),
Downloaded(R.drawable.ic_synced)
}

private fun updateFileDownloadIndicator(state: FileDownloadIndicator, file: OCFile) {
ocFileListFragment?.adapter?.updateFileIndicator(state.iconId, file)
}

// region FolderDownloadWorker receivers
@Suppress("ReturnCount")
private fun getFolderFromFolderDownloadWorker(intent: Intent): OCFile? {
val id = intent.getLongExtra(FolderDownloadEventBroadcaster.EXTRA_FILE_ID, -1L)
if (id == -1L) {
Log_OC.e(TAG, "invalid id received")
return null
}

val folder = storageManager.getFileById(id)
if (folder == null) {
Log_OC.e(TAG, "folder not exists")
return null
}

return folder
}

private class FolderDownloadStartedReceiver : BroadcastReceiver() {
private inner class FolderDownloadStartedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log_OC.d(TAG, "download worker started")
val folder = getFolderFromFolderDownloadWorker(intent) ?: return
updateFileDownloadIndicator(FileDownloadIndicator.Downloading, folder)
}
}

private inner class FolderDownloadCompletedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log_OC.d(TAG, "download worker finished")
val folder = getFolderFromFolderDownloadWorker(intent) ?: return
updateFileDownloadIndicator(FileDownloadIndicator.Downloaded, folder)
}
}
// endregion

val id = intent.getLongExtra(FolderDownloadEventBroadcaster.EXTRA_FILE_ID, -1L)
if (id == -1L) {
Log_OC.e(TAG, "invalid id received")
return
}
// region FileDownloadWorker receivers
private fun getFileFromFileDownloadWorker(intent: Intent): OCFile? {
val remotePath = intent.getStringExtra(FileDownloadEventBroadcaster.EXTRA_REMOTE_PATH)
val file = fileDataStorageManager.getFileByDecryptedRemotePath(remotePath) ?: return null
return file
}

val folder = storageManager.getFileById(id)
if (folder == null) {
Log_OC.e(TAG, "folder not exists")
return
private inner class FileDownloadStartedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log_OC.d(TAG, "download worker started")
getFileFromFileDownloadWorker(intent)?.let {
updateFileDownloadIndicator(FileDownloadIndicator.Downloading, it)
}

ocFileListFragment?.adapter?.notifyItemChanged(folder)
handleDownloadWorkerState()
}
}

/**
* Class waiting for broadcast events from the [FileDownloadWorker] service.
*
*
* Updates the UI when a download is started or finished, provided that it is relevant for the current folder.
*/
private inner class FileDownloadCompletedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
Log_OC.d(TAG, "file download completed received")
getFileFromFileDownloadWorker(intent)?.let {
updateFileDownloadIndicator(FileDownloadIndicator.Downloaded, it)
}

fileDownloadProgressListener = null

if (fileIDForImmediatePreview == -1L) {
Expand Down Expand Up @@ -1857,6 +1883,7 @@ class FileDisplayActivity :
return accountName != null && account != null && accountName == account.name
}
}
// endregion

fun browseToRoot() {
val listOfFiles = this.listOfFilesFragment
Expand Down Expand Up @@ -2356,7 +2383,7 @@ class FileDisplayActivity :
private fun requestForDownload() {
val user = user.orElseThrow(Supplier { RuntimeException() })
mWaitingToPreview?.let {
FileDownloadHelper.Companion.instance().downloadFileIfNotStartedBefore(user, it)
FileDownloadHelper.instance().downloadFileIfNotStartedBefore(user, it)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.model.OfflineOperationType;
import com.nextcloud.utils.extensions.OCFileExtensionsKt;
import com.nextcloud.utils.extensions.ViewExtensionsKt;
import com.nextcloud.utils.mdm.MDMConfig;
import com.owncloud.android.MainApp;
Expand Down Expand Up @@ -871,13 +870,6 @@ public void swapDirectory(
}

public void updateAdapter(List<OCFile> newFiles, OCFile directory) {
boolean hasSameContent = OCFileExtensionsKt.hasSameContentAs(mFiles, newFiles);

if (hasSameContent) {
Log_OC.d(TAG, "same data passed skipping update");
return;
}

Log_OC.d(TAG, "updating the adapter");

mFiles = new ArrayList<>(newFiles);
Expand Down Expand Up @@ -1085,6 +1077,27 @@ public void setCurrentDirectory(OCFile folder) {
currentDirectory = folder;
}

// payload only for local file indicator
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (!payloads.isEmpty() && payloads.get(0) instanceof Integer iconId && holder instanceof ListViewHolder listViewHolder) {
listViewHolder.getLocalFileIndicator().setImageResource(iconId);
listViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
// skip full rebind
return;
}
super.onBindViewHolder(holder, position, payloads);
}

public void updateFileIndicator(int iconId, OCFile file) {
if (file == null) return;

int position = getItemPosition(file);
if (position != -1) {
notifyItemChanged(position, iconId);
}
}

public void cleanup() {
ocFileListDelegate.cleanup();
helper.cleanup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import com.nextcloud.client.jobs.gallery.GalleryImageGenerationListener
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.OCFileUtils
import com.nextcloud.utils.extensions.getSubfiles
import com.nextcloud.utils.extensions.makeRounded
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.mdm.MDMConfig
Expand All @@ -45,7 +44,6 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@Suppress("LongParameterList", "TooManyFunctions")
class OCFileListDelegate(
Expand Down Expand Up @@ -337,13 +335,6 @@ class OCFileListDelegate(
}
}

private suspend fun isFolderFullyDownloaded(file: OCFile): Boolean = withContext(Dispatchers.IO) {
file.isFolder &&
storageManager.getSubfiles(file.fileId, user.accountName)
.takeIf { it.isNotEmpty() }
?.all { it.isDown } == true
}

private fun isSynchronizing(file: OCFile): Boolean {
val operationsServiceBinder = transferServiceGetter.operationsServiceBinder
val fileDownloadHelper = FileDownloadHelper.instance()
Expand All @@ -354,28 +345,24 @@ class OCFileListDelegate(
}

private fun showLocalFileIndicator(file: OCFile, holder: ListViewHolder) {
ioScope.launch {
val isFullyDownloaded = isFolderFullyDownloaded(file)
val isSyncing = isSynchronizing(file)
val hasConflict = (file.etagInConflict != null)
val isDown = file.isDown

val icon = when {
isSyncing -> R.drawable.ic_synchronizing
hasConflict -> R.drawable.ic_synchronizing_error
isDown || isFullyDownloaded -> R.drawable.ic_synced
else -> null
}
val isFullyDownloaded = storageManager.fileDao.isFolderFullyDownloaded(file.fileId, user.accountName)
val isSyncing = isSynchronizing(file)
val hasConflict = (file.etagInConflict != null)
val isDown = file.isDown

val icon = when {
isSyncing -> R.drawable.ic_synchronizing
hasConflict -> R.drawable.ic_synchronizing_error
isDown || isFullyDownloaded -> R.drawable.ic_synced
else -> null
}

withContext(Dispatchers.Main) {
holder.localFileIndicator.run {
if (icon != null && showMetadata) {
setImageResource(icon)
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
}
holder.localFileIndicator.run {
if (icon != null && showMetadata) {
setImageResource(icon)
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
}
}
Expand Down
Loading