diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/ContentUriLastModifiedResolver.kt b/app/src/main/java/com/owncloud/android/ui/asynctasks/ContentUriLastModifiedResolver.kt new file mode 100644 index 000000000000..1d4bcf43bf21 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/asynctasks/ContentUriLastModifiedResolver.kt @@ -0,0 +1,56 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.asynctasks + +import android.content.ContentResolver +import android.net.Uri +import android.provider.DocumentsContract +import android.provider.MediaStore +import android.system.Os +import android.system.OsConstants +import android.system.StructStat +import java.util.concurrent.TimeUnit + +class ContentUriLastModifiedResolver(private val contentResolver: ContentResolver) { + companion object { + private val TIMESTAMP_COLUMNS = listOf( + DocumentsContract.Document.COLUMN_LAST_MODIFIED to TimeUnit.MILLISECONDS, + MediaStore.MediaColumns.DATE_MODIFIED to TimeUnit.SECONDS + ) + } + + fun resolve(uri: Uri): Long = fromMetadata(uri).takeIf { it > 0L } ?: fromFileDescriptor(uri) + + private fun fromMetadata(uri: Uri): Long = runCatching { + contentResolver.query(uri, null, null, null, null)?.use { cursor -> + if (!cursor.moveToFirst()) return@use null + + TIMESTAMP_COLUMNS.firstNotNullOfOrNull { (column, unit) -> + runCatching { + cursor.getColumnIndex(column) + .takeIf { it >= 0 && !cursor.isNull(it) } + ?.let(cursor::getLong) + ?.takeIf { it > 0L } + ?.let(unit::toMillis) + }.getOrNull() + } + } + }.getOrNull() ?: 0L + + private fun fromFileDescriptor(uri: Uri): Long = runCatching { + contentResolver.openFileDescriptor(uri, "r")?.use { pfd -> + Os.fstat(pfd.fileDescriptor) + .takeIf { OsConstants.S_ISREG(it.st_mode) } + ?.lastModifiedMillis() + ?.takeIf { it > 0L } + } + }.getOrNull() ?: 0L + + private fun StructStat.lastModifiedMillis(): Long = + TimeUnit.SECONDS.toMillis(st_mtim.tv_sec) + TimeUnit.NANOSECONDS.toMillis(st_mtim.tv_nsec) +} diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.kt b/app/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.kt index 03db1eb5a3fe..b329b35406d7 100644 --- a/app/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.kt +++ b/app/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.kt @@ -9,7 +9,6 @@ package com.owncloud.android.ui.asynctasks import android.content.ContentResolver import android.content.Context import android.net.Uri -import android.provider.DocumentsContract import android.widget.Toast import com.nextcloud.client.account.User import com.nextcloud.client.jobs.upload.FileUploadHelper @@ -66,11 +65,12 @@ class CopyAndUploadContentUrisTask( val localPaths = arrayOfNulls(sourceUris.size) val resolvedRemotePaths = arrayOfNulls(sourceUris.size) var currentTempPath: String? = null + val lastModifiedResolver = ContentUriLastModifiedResolver(contentResolver) return try { sourceUris.forEachIndexed { index, uri -> val remotePath = remotePaths[index] - val lastModified = queryLastModified(contentResolver, uri) + val lastModified = lastModifiedResolver.resolve(uri) currentTempPath = "${FileStorageUtils.getTemporalPath(user.accountName)}$remotePath" val cacheFile = File(currentTempPath) @@ -130,18 +130,11 @@ class CopyAndUploadContentUrisTask( } } - private fun queryLastModified(contentResolver: ContentResolver, uri: Uri): Long = runCatching { - contentResolver.query(uri, null, null, null, null)?.use { cursor -> - if (!cursor.moveToFirst()) return@use 0L - val col = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED) - if (col >= 0) cursor.getLong(col) else 0L - } ?: 0L - }.getOrDefault(0L) - private fun applyLastModified(file: File, lastModified: Long) { - if (lastModified == 0L) return - runCatching { file.setLastModified(lastModified) } - .onFailure { Log_OC.w(TAG, "Could not set lastModified on cache file: ${it.message}") } + if (lastModified <= 0L) return + if (!file.setLastModified(lastModified)) { + Log_OC.w(TAG, "Could not set lastModified on cache file: ${file.path}") + } } private fun dispatchResult(result: ResultCode) {