From 6a4885a334ecf38f5c004d3ff366fa450c37bf11 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 24 Apr 2026 14:52:13 +0200 Subject: [PATCH 1/5] Rename .java to .kt Signed-off-by: alperozturk96 --- .../asynctasks/{GallerySearchTask.java => GallerySearchTask.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/owncloud/android/ui/asynctasks/{GallerySearchTask.java => GallerySearchTask.kt} (100%) diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt similarity index 100% rename from app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java rename to app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt From 13d40d696ad2759a471522de99fe5f7caa3b34c9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 24 Apr 2026 14:52:13 +0200 Subject: [PATCH 2/5] convert gallery search task to kotlin and use coroutines Signed-off-by: alperozturk96 --- .../ui/asynctasks/GallerySearchTask.kt | 300 ++++++++---------- 1 file changed, 140 insertions(+), 160 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt index 183d828c16f6..e27aed10ef6e 100644 --- a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt +++ b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt @@ -1,225 +1,205 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ -package com.owncloud.android.ui.asynctasks; - -import android.os.AsyncTask; - -import com.nextcloud.client.account.User; -import com.owncloud.android.BuildConfig; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.files.SearchRemoteOperation; -import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.operations.RefreshFolderOperation; -import com.owncloud.android.ui.fragment.GalleryFragment; -import com.owncloud.android.utils.FileStorageUtils; - -import java.lang.ref.WeakReference; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public class GallerySearchTask extends AsyncTask { - - private final User user; - private final WeakReference photoFragmentWeakReference; - private final FileDataStorageManager storageManager; - private final int limit; - private final long endDate; - - public GallerySearchTask(GalleryFragment photoFragment, - User user, - FileDataStorageManager storageManager, - long endDate, - int limit) { - this.user = user; - this.photoFragmentWeakReference = new WeakReference<>(photoFragment); - this.storageManager = storageManager; - this.endDate = endDate; - this.limit = limit; - } - - @Override - protected GallerySearchTask.Result doInBackground(Void... voids) { - if (photoFragmentWeakReference.get() == null) { - return new Result(false, false, -1); - } - GalleryFragment photoFragment = photoFragmentWeakReference.get(); - - if (isCancelled()) { - return new Result(false, false, -1); - } else { - OCCapability ocCapability = storageManager.getCapability(user.getAccountName()); - - SearchRemoteOperation searchRemoteOperation = new SearchRemoteOperation("", - SearchRemoteOperation.SearchType.GALLERY_SEARCH, - false, - ocCapability); - - searchRemoteOperation.setLimit(limit); - searchRemoteOperation.setEndDate(endDate); - - //workaround to keep SearchRemoteOperation functioning correctly even if we don't actively use startDate - searchRemoteOperation.setStartDate(0L); - - if (photoFragment.getContext() != null) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - Log_OC.d(this, - "Start gallery search since " - + dateFormat.format(new Date(endDate * 1000L)) - + " with limit: " - + limit); - RemoteOperationResult result = searchRemoteOperation.execute(user, photoFragment.getContext()); +package com.owncloud.android.ui.asynctasks + +import androidx.lifecycle.lifecycleScope +import com.nextcloud.client.account.User +import com.owncloud.android.BuildConfig +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.SearchRemoteOperation +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.operations.RefreshFolderOperation +import com.owncloud.android.ui.fragment.GalleryFragment +import com.owncloud.android.utils.FileStorageUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class GallerySearchTask( + private val fragment: GalleryFragment, + private val user: User, + private val storageManager: FileDataStorageManager, + private val endDate: Long, + private val limit: Int +) { + + fun execute() { + fragment.lifecycleScope.launch(Dispatchers.IO) { + val result = if (!isActive) { + Result(false, false, -1) + } else { + val ocCapability = storageManager.getCapability(user.accountName) + val searchRemoteOperation = SearchRemoteOperation( + "", + SearchRemoteOperation.SearchType.GALLERY_SEARCH, + false, + ocCapability + ) + + searchRemoteOperation.setLimit(limit) + searchRemoteOperation.setEndDate(endDate) + + //workaround to keep SearchRemoteOperation functioning correctly even if we don't actively use startDate + searchRemoteOperation.setStartDate(0L) + + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + Log_OC.d( + this, + ("Start gallery search since " + + dateFormat.format(Date(endDate * 1000L)) + + " with limit: " + + limit) + ) + + val context = fragment.context + if (context == null) { + return@launch Result(false, false, -1) + } + val result: RemoteOperationResult<*> = searchRemoteOperation.execute(user, context) - if (result.isSuccess()) { - long lastTimeStamp = findLastTimestamp(result.getData()); + if (result.isSuccess) { + val lastTimeStamp = findLastTimestamp(result.data) //query the local storage based on the lastTimeStamp retrieved, not by 1970-01-01 - boolean emptySearch = parseMedia(lastTimeStamp, this.endDate, result.getData()); - return new Result(result.isSuccess(), emptySearch, lastTimeStamp); + val emptySearch = parseMedia(lastTimeStamp, endDate, result.data) + Result(result.isSuccess(), emptySearch, lastTimeStamp) } else { - return new Result(false, false, -1); + Result(false, false, -1) } - } else { - return new Result(false, false, -1); } - } - } - @Override - protected void onPostExecute(GallerySearchTask.Result result) { - if (photoFragmentWeakReference.get() != null) { - GalleryFragment photoFragment = photoFragmentWeakReference.get(); - photoFragment.searchCompleted(result.emptySearch, result.lastTimestamp); + withContext(Dispatchers.Main) { + fragment.searchCompleted(result.emptySearch, result.lastTimestamp) + } } } - private long findLastTimestamp(ArrayList remoteFiles) { - int lastPosition = remoteFiles.size() - 1; + + private fun findLastTimestamp(remoteFiles: ArrayList): Long { + val lastPosition = remoteFiles.size - 1 if (lastPosition < 0) { - return -1; + return -1 } - RemoteFile lastFile = remoteFiles.get(lastPosition); - return lastFile.getModifiedTimestamp() / 1000; + val lastFile = remoteFiles[lastPosition] + return lastFile.modifiedTimestamp / 1000 } - private boolean parseMedia(long startDate, long endDate, List remoteFiles) { - - List localFiles = storageManager.getGalleryItems(startDate * 1000L, endDate * 1000L); + private fun parseMedia(startDate: Long, endDate: Long, remoteFiles: MutableList): Boolean { + val localFiles = storageManager.getGalleryItems(startDate * 1000L, endDate * 1000L) if (BuildConfig.DEBUG) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - Log_OC.d(this, - "parseMedia - start: " - + dateFormat.format(new Date(startDate * 1000L)) - + " - " - + dateFormat.format(new Date(endDate * 1000L))); - - for (OCFile localFile : localFiles) { - Log_OC.d(this, - "local file: modified: " - + dateFormat.format(new Date(localFile.getModificationTimestamp())) - + " path: " - + localFile.getRemotePath()); + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + Log_OC.d( + this, + ("parseMedia - start: " + + dateFormat.format(Date(startDate * 1000L)) + + " - " + + dateFormat.format(Date(endDate * 1000L))) + ) + + for (localFile in localFiles) { + Log_OC.d( + this, + ("local file: modified: " + + dateFormat.format(Date(localFile.modificationTimestamp)) + + " path: " + + localFile.remotePath) + ) } } - Map localFilesMap = RefreshFolderOperation.prefillLocalFilesMap(null, localFiles); + val localFilesMap = RefreshFolderOperation.prefillLocalFilesMap(null, localFiles) - long filesAdded = 0, filesUpdated = 0, unchangedFiles = 0; + var filesAdded: Long = 0 + var filesUpdated: Long = 0 + var unchangedFiles: Long = 0 - for (Object file : remoteFiles) { - if (!(file instanceof RemoteFile remoteFile)) { - Log_OC.d(this, "object file is not remote file"); - continue; + for (file in remoteFiles) { + if (file !is RemoteFile) { + Log_OC.d(this, "object file is not remote file") + continue } - final OCFile existingFile = storageManager.getFileByDecryptedRemotePath(remoteFile.getRemotePath()); + val existingFile = storageManager.getFileByDecryptedRemotePath(file.remotePath) // add missing values from local storage to prevent override with null values if (existingFile != null) { - final var imageDimension = existingFile.getImageDimension(); + val imageDimension = existingFile.imageDimension if (imageDimension != null) { - remoteFile.setImageDimension(existingFile.getImageDimension()); + file.imageDimension = existingFile.imageDimension } - remoteFile.setLocalId(existingFile.getLocalId()); - remoteFile.setCreationTimestamp(existingFile.getCreationTimestamp()); - remoteFile.setUploadTimestamp(existingFile.getUploadTimestamp()); + file.localId = existingFile.localId + file.creationTimestamp = existingFile.creationTimestamp + file.uploadTimestamp = existingFile.uploadTimestamp } - OCFile ocFile = FileStorageUtils.fillOCFile(remoteFile); + val ocFile = FileStorageUtils.fillOCFile(file) if (BuildConfig.DEBUG) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - Log_OC.d(this, - "remote file: modified: " - + dateFormat.format(new Date(ocFile.getModificationTimestamp())) - + " path: " - + ocFile.getRemotePath()); + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + Log_OC.d( + this, + ("remote file: modified: " + + dateFormat.format(Date(ocFile.modificationTimestamp)) + + " path: " + + ocFile.remotePath) + ) } - OCFile localFile = localFilesMap.remove(ocFile.getRemotePath()); + val localFile = localFilesMap.remove(ocFile.remotePath) if (localFile == null) { // add new file - storageManager.saveFile(ocFile); - filesAdded++; - } else if (!localFile.getEtag().equals(ocFile.getEtag())) { + storageManager.saveFile(ocFile) + filesAdded++ + } else if (localFile.etag != ocFile.etag) { // update file - ocFile.setLastSyncDateForData(System.currentTimeMillis()); - storageManager.saveFile(ocFile); - filesUpdated++; + ocFile.lastSyncDateForData = System.currentTimeMillis() + storageManager.saveFile(ocFile) + filesUpdated++ } else { - unchangedFiles++; + unchangedFiles++ } - } // existing files to remove - long filesDeleted = localFilesMap.size(); + val filesDeleted = localFilesMap.size.toLong() - for (OCFile file : localFilesMap.values()) { + for (file in localFilesMap.values) { if (BuildConfig.DEBUG) { - Log_OC.d(this, "Gallery Sync: File deleted " + file.getRemotePath()); + Log_OC.d(this, "Gallery Sync: File deleted " + file.remotePath) } - storageManager.removeFile(file, true, true); + storageManager.removeFile(file, true, true) } if (BuildConfig.DEBUG) { - Log_OC.d(this, "Gallery search result:" + - " new: " + filesAdded + - " updated: " + filesUpdated + - " deleted: " + filesDeleted + - " unchanged: " + unchangedFiles); + Log_OC.d( + this, "Gallery search result:" + + " new: " + filesAdded + + " updated: " + filesUpdated + + " deleted: " + filesDeleted + + " unchanged: " + unchangedFiles + ) } - return filesAdded <= 0 && filesUpdated <= 0 && filesDeleted <= 0; + return filesAdded <= 0 && filesUpdated <= 0 && filesDeleted <= 0 } - public static class Result { - public boolean success; - public boolean emptySearch; - public long lastTimestamp; - - public Result(boolean success, boolean emptySearch, long lastTimestamp) { - this.success = success; - this.emptySearch = emptySearch; - this.lastTimestamp = lastTimestamp; - } - } + data class Result(var success: Boolean, var emptySearch: Boolean, var lastTimestamp: Long) } From f536eee932758caa6d16de3bef06e837ca7bb918 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 24 Apr 2026 15:01:24 +0200 Subject: [PATCH 3/5] convert gallery search task to kotlin and use coroutines Signed-off-by: alperozturk96 --- .../ui/asynctasks/GallerySearchTask.kt | 238 ++++++++---------- .../android/ui/fragment/GalleryFragment.kt | 15 +- 2 files changed, 118 insertions(+), 135 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt index e27aed10ef6e..d3d4a0660875 100644 --- a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt +++ b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.kt @@ -1,10 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2026 Alper Ozturk - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.ui.asynctasks @@ -12,6 +10,7 @@ import androidx.lifecycle.lifecycleScope import com.nextcloud.client.account.User import com.owncloud.android.BuildConfig import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.files.SearchRemoteOperation @@ -20,6 +19,7 @@ import com.owncloud.android.operations.RefreshFolderOperation import com.owncloud.android.ui.fragment.GalleryFragment import com.owncloud.android.utils.FileStorageUtils import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -34,172 +34,156 @@ class GallerySearchTask( private val endDate: Long, private val limit: Int ) { + companion object { + private const val NO_TIMESTAMP = -1L + private const val DATE_FORMAT = "yyyy-MM-dd HH:mm:ss" + private const val MILLIS_PER_SECOND = 1000L + } - fun execute() { - fragment.lifecycleScope.launch(Dispatchers.IO) { - val result = if (!isActive) { - Result(false, false, -1) - } else { - val ocCapability = storageManager.getCapability(user.accountName) - val searchRemoteOperation = SearchRemoteOperation( - "", - SearchRemoteOperation.SearchType.GALLERY_SEARCH, - false, - ocCapability - ) - - searchRemoteOperation.setLimit(limit) - searchRemoteOperation.setEndDate(endDate) - - //workaround to keep SearchRemoteOperation functioning correctly even if we don't actively use startDate - searchRemoteOperation.setStartDate(0L) - - val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) - Log_OC.d( - this, - ("Start gallery search since " - + dateFormat.format(Date(endDate * 1000L)) - + " with limit: " - + limit) - ) - - val context = fragment.context - if (context == null) { - return@launch Result(false, false, -1) - } - val result: RemoteOperationResult<*> = searchRemoteOperation.execute(user, context) - - if (result.isSuccess) { - val lastTimeStamp = findLastTimestamp(result.data) + fun execute(): Job = fragment.lifecycleScope.launch(Dispatchers.IO) { + if (!isActive) return@launch - //query the local storage based on the lastTimeStamp retrieved, not by 1970-01-01 - val emptySearch = parseMedia(lastTimeStamp, endDate, result.data) - Result(result.isSuccess(), emptySearch, lastTimeStamp) - } else { - Result(false, false, -1) - } - } + val context = fragment.context ?: return@launch + val result = performSearch(context) - withContext(Dispatchers.Main) { - fragment.searchCompleted(result.emptySearch, result.lastTimestamp) - } + withContext(Dispatchers.Main) { + fragment.searchCompleted(result.emptySearch, result.lastTimestamp) } } + @Suppress("DEPRECATION") + private fun performSearch(context: android.content.Context): Result { + val ocCapability = storageManager.getCapability(user.accountName) + val searchOperation = buildSearchOperation(ocCapability) - private fun findLastTimestamp(remoteFiles: ArrayList): Long { - val lastPosition = remoteFiles.size - 1 + logSearchStart() - if (lastPosition < 0) { - return -1 + val operationResult = searchOperation.execute(user, context) + return if (operationResult.isSuccess) { + handleSuccess(operationResult) + } else { + Result(false, false, NO_TIMESTAMP) } + } + + private fun buildSearchOperation( + ocCapability: com.owncloud.android.lib.resources.status.OCCapability + ): SearchRemoteOperation = + SearchRemoteOperation("", SearchRemoteOperation.SearchType.GALLERY_SEARCH, false, ocCapability).apply { + setLimit(limit) + setEndDate(endDate) + // workaround to keep SearchRemoteOperation functioning correctly even if we don't actively use startDate + setStartDate(0L) + } + + private fun logSearchStart() { + val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.US) + Log_OC.d( + this, + "Start gallery search since ${dateFormat.format(Date(endDate * MILLIS_PER_SECOND))} with limit: $limit" + ) + } - val lastFile = remoteFiles[lastPosition] - return lastFile.modifiedTimestamp / 1000 + @Suppress("DEPRECATION") + private fun handleSuccess(operationResult: RemoteOperationResult<*>): Result { + val remoteFiles = operationResult.data.filterIsInstance() + val lastTimestamp = findLastTimestamp(remoteFiles) + val emptySearch = parseMedia(lastTimestamp, endDate, remoteFiles) + return Result(true, emptySearch, lastTimestamp) } - private fun parseMedia(startDate: Long, endDate: Long, remoteFiles: MutableList): Boolean { - val localFiles = storageManager.getGalleryItems(startDate * 1000L, endDate * 1000L) + private fun findLastTimestamp(remoteFiles: List): Long = + remoteFiles.lastOrNull()?.modifiedTimestamp?.div(MILLIS_PER_SECOND) ?: NO_TIMESTAMP - if (BuildConfig.DEBUG) { - val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) - Log_OC.d( - this, - ("parseMedia - start: " - + dateFormat.format(Date(startDate * 1000L)) - + " - " - + dateFormat.format(Date(endDate * 1000L))) - ) + private fun parseMedia(startDate: Long, endDate: Long, remoteFiles: List): Boolean { + val localFiles = storageManager.getGalleryItems(startDate * MILLIS_PER_SECOND, endDate * MILLIS_PER_SECOND) - for (localFile in localFiles) { - Log_OC.d( - this, - ("local file: modified: " - + dateFormat.format(Date(localFile.modificationTimestamp)) - + " path: " - + localFile.remotePath) - ) - } + if (BuildConfig.DEBUG) { + logParseMediaRange(startDate, endDate, localFiles) } val localFilesMap = RefreshFolderOperation.prefillLocalFilesMap(null, localFiles) - var filesAdded: Long = 0 - var filesUpdated: Long = 0 - var unchangedFiles: Long = 0 + var filesAdded = 0L + var filesUpdated = 0L + var unchangedFiles = 0L for (file in remoteFiles) { - if (file !is RemoteFile) { - Log_OC.d(this, "object file is not remote file") - continue - } - - val existingFile = storageManager.getFileByDecryptedRemotePath(file.remotePath) - - // add missing values from local storage to prevent override with null values - if (existingFile != null) { - val imageDimension = existingFile.imageDimension - if (imageDimension != null) { - file.imageDimension = existingFile.imageDimension - } - file.localId = existingFile.localId - file.creationTimestamp = existingFile.creationTimestamp - file.uploadTimestamp = existingFile.uploadTimestamp - } - + enrichFromLocalStorage(file) val ocFile = FileStorageUtils.fillOCFile(file) if (BuildConfig.DEBUG) { - val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) - Log_OC.d( - this, - ("remote file: modified: " - + dateFormat.format(Date(ocFile.modificationTimestamp)) - + " path: " - + ocFile.remotePath) - ) + logRemoteFile(ocFile) } val localFile = localFilesMap.remove(ocFile.remotePath) + when { + localFile == null -> { + storageManager.saveFile(ocFile) + filesAdded++ + } + + localFile.etag != ocFile.etag -> { + ocFile.lastSyncDateForData = System.currentTimeMillis() + storageManager.saveFile(ocFile) + filesUpdated++ + } - if (localFile == null) { - // add new file - storageManager.saveFile(ocFile) - filesAdded++ - } else if (localFile.etag != ocFile.etag) { - // update file - ocFile.lastSyncDateForData = System.currentTimeMillis() - storageManager.saveFile(ocFile) - filesUpdated++ - } else { - unchangedFiles++ + else -> unchangedFiles++ } } - // existing files to remove val filesDeleted = localFilesMap.size.toLong() - for (file in localFilesMap.values) { if (BuildConfig.DEBUG) { - Log_OC.d(this, "Gallery Sync: File deleted " + file.remotePath) + Log_OC.d(this, "Gallery Sync: File deleted ${file.remotePath}") } - storageManager.removeFile(file, true, true) } if (BuildConfig.DEBUG) { Log_OC.d( - this, "Gallery search result:" + - " new: " + filesAdded + - " updated: " + filesUpdated + - " deleted: " + filesDeleted + - " unchanged: " + unchangedFiles + this, + "Gallery search result: new: $filesAdded updated: $filesUpdated" + + " deleted: $filesDeleted unchanged: $unchangedFiles" ) } return filesAdded <= 0 && filesUpdated <= 0 && filesDeleted <= 0 } - data class Result(var success: Boolean, var emptySearch: Boolean, var lastTimestamp: Long) -} + private fun enrichFromLocalStorage(file: RemoteFile) { + val existingFile = storageManager.getFileByDecryptedRemotePath(file.remotePath) ?: return + existingFile.imageDimension?.let { file.imageDimension = it } + file.localId = existingFile.localId + file.creationTimestamp = existingFile.creationTimestamp + file.uploadTimestamp = existingFile.uploadTimestamp + } + + private fun logParseMediaRange(startDate: Long, endDate: Long, localFiles: List) { + val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.US) + Log_OC.d( + this, + "parseMedia - start: ${dateFormat.format(Date(startDate * MILLIS_PER_SECOND))}" + + " - ${dateFormat.format(Date(endDate * MILLIS_PER_SECOND))}" + ) + for (localFile in localFiles) { + Log_OC.d( + this, + "local file: modified: ${dateFormat.format(Date(localFile.modificationTimestamp))}" + + " path: ${localFile.remotePath}" + ) + } + } + private fun logRemoteFile(ocFile: OCFile) { + val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.US) + Log_OC.d( + this, + "remote file: modified: ${dateFormat.format(Date(ocFile.modificationTimestamp))}" + + " path: ${ocFile.remotePath}" + ) + } + + data class Result(val success: Boolean, val emptySearch: Boolean, val lastTimestamp: Long) +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt index 0e1db746d0ed..77124fc45c5b 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt @@ -13,7 +13,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.res.Configuration -import android.os.AsyncTask import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -29,6 +28,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.nextcloud.utils.extensions.getParcelableArgument +import kotlinx.coroutines.Job import com.nextcloud.utils.extensions.getTypedActivity import com.owncloud.android.BuildConfig import com.owncloud.android.R @@ -50,7 +50,7 @@ class GalleryFragment : OCFileListFragment(), GalleryFragmentBottomSheetActions { var isPhotoSearchQueryRunning: Boolean = false - private var photoSearchTask: AsyncTask? = null + private var photoSearchTask: Job? = null private var endDate: Long = 0 private val limit = 150 private var adapter: GalleryAdapter? = null @@ -125,7 +125,7 @@ class GalleryFragment : override fun onDestroyView() { if (photoSearchTask != null) { - photoSearchTask?.cancel(true) + photoSearchTask?.cancel() photoSearchTask = null } @@ -140,7 +140,7 @@ class GalleryFragment : override fun onPause() { super.onPause() - photoSearchTask?.cancel(true) + photoSearchTask?.cancel() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -245,8 +245,8 @@ class GalleryFragment : private fun searchAndDisplay() { if (!isPhotoSearchQueryRunning && endDate <= 0) { - // fix an issue when the method is called after loading the gallery and pressing play on a movie (--> endDate <= 0) - // to avoid reloading the gallery, check if endDate has already a value which is not -1 or 0 (which generally means some kind of reset/init) + // fix an issue when the method is called after loading the gallery and pressing play on a movie + // to avoid reloading, check if endDate has already a value which is not -1 or 0 endDate = System.currentTimeMillis() / 1000 isPhotoSearchQueryRunning = true runGallerySearchTask() @@ -319,8 +319,7 @@ class GalleryFragment : mContainerActivity.getStorageManager(), endDate, limit - ) - .execute() + ).execute() } private fun loadMoreWhenEndReached(recyclerView: RecyclerView, dy: Int) { From f81404b43d38d7c0209d2622a26ffa0a974342df Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 24 Apr 2026 15:25:02 +0200 Subject: [PATCH 4/5] fix scrolling search Signed-off-by: alperozturk96 --- .../com/owncloud/android/ui/fragment/GalleryFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt index 77124fc45c5b..1abb0f31aba7 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt @@ -323,13 +323,13 @@ class GalleryFragment : } private fun loadMoreWhenEndReached(recyclerView: RecyclerView, dy: Int) { - if (dy > 0 && !isPhotoSearchQueryRunning) { - // scrolled vertical space not bigger than 0 or still searching + if (dy <= 0 || isPhotoSearchQueryRunning) { + Log_OC.d(TAG, "scrolling up or search query already active, do not search gallery") return } if (recyclerView.layoutManager !is GridLayoutManager) { - Log_OC.e(TAG, "can't load more layout manager is not grid") + Log_OC.e(TAG, "can't load more; layout manager is not LinearLayoutManager or GridLayoutManager") return } From f4a0ae095ecbf5366100de99c79c7d90f21f5443 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Wed, 29 Apr 2026 10:35:38 +0200 Subject: [PATCH 5/5] Apply suggestion from @tobiasKaminsky Signed-off-by: Tobias Kaminsky --- .../java/com/owncloud/android/ui/fragment/GalleryFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt index 1abb0f31aba7..f231b05481cb 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt @@ -324,7 +324,7 @@ class GalleryFragment : private fun loadMoreWhenEndReached(recyclerView: RecyclerView, dy: Int) { if (dy <= 0 || isPhotoSearchQueryRunning) { - Log_OC.d(TAG, "scrolling up or search query already active, do not search gallery") +// scrolling up or search query already active, do not search gallery return }