Skip to content

Commit 3348df5

Browse files
Merge pull request #17704 from nextcloud/fix/modification-date
fix(copy-upload): modification date
2 parents 5867304 + 54d67a2 commit 3348df5

3 files changed

Lines changed: 63 additions & 14 deletions

File tree

‎app/detekt.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ complexity:
6767
threshold: 600
6868
LongMethod:
6969
active: true
70-
threshold: 60
70+
threshold: 120
7171
excludes: ['**/androidTest/**']
7272
LongParameterList:
7373
active: false
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.ui.asynctasks
9+
10+
import android.content.ContentResolver
11+
import android.net.Uri
12+
import android.provider.DocumentsContract
13+
import android.provider.MediaStore
14+
import android.system.Os
15+
import android.system.OsConstants
16+
import android.system.StructStat
17+
import java.util.concurrent.TimeUnit
18+
19+
class ContentUriLastModifiedResolver(private val contentResolver: ContentResolver) {
20+
companion object {
21+
private val TIMESTAMP_COLUMNS = listOf(
22+
DocumentsContract.Document.COLUMN_LAST_MODIFIED to TimeUnit.MILLISECONDS,
23+
MediaStore.MediaColumns.DATE_MODIFIED to TimeUnit.SECONDS
24+
)
25+
}
26+
27+
fun resolve(uri: Uri): Long = fromMetadata(uri).takeIf { it > 0L } ?: fromFileDescriptor(uri)
28+
29+
private fun fromMetadata(uri: Uri): Long = runCatching {
30+
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
31+
if (!cursor.moveToFirst()) return@use null
32+
33+
TIMESTAMP_COLUMNS.firstNotNullOfOrNull { (column, unit) ->
34+
runCatching {
35+
cursor.getColumnIndex(column)
36+
.takeIf { it >= 0 && !cursor.isNull(it) }
37+
?.let(cursor::getLong)
38+
?.takeIf { it > 0L }
39+
?.let(unit::toMillis)
40+
}.getOrNull()
41+
}
42+
}
43+
}.getOrNull() ?: 0L
44+
45+
private fun fromFileDescriptor(uri: Uri): Long = runCatching {
46+
contentResolver.openFileDescriptor(uri, "r")?.use { pfd ->
47+
Os.fstat(pfd.fileDescriptor)
48+
.takeIf { OsConstants.S_ISREG(it.st_mode) }
49+
?.lastModifiedMillis()
50+
?.takeIf { it > 0L }
51+
}
52+
}.getOrNull() ?: 0L
53+
54+
private fun StructStat.lastModifiedMillis(): Long =
55+
TimeUnit.SECONDS.toMillis(st_mtim.tv_sec) + TimeUnit.NANOSECONDS.toMillis(st_mtim.tv_nsec)
56+
}

‎app/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.kt‎

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package com.owncloud.android.ui.asynctasks
99
import android.content.ContentResolver
1010
import android.content.Context
1111
import android.net.Uri
12-
import android.provider.DocumentsContract
1312
import android.widget.Toast
1413
import com.nextcloud.client.account.User
1514
import com.nextcloud.client.jobs.upload.FileUploadHelper
@@ -65,11 +64,12 @@ class CopyAndUploadContentUrisTask(
6564
val localPaths = arrayOfNulls<String>(sourceUris.size)
6665
val resolvedRemotePaths = arrayOfNulls<String>(sourceUris.size)
6766
var currentTempPath: String? = null
67+
val lastModifiedResolver = ContentUriLastModifiedResolver(contentResolver)
6868

6969
return try {
7070
sourceUris.forEachIndexed { index, uri ->
7171
val remotePath = remotePaths[index]
72-
val lastModified = queryLastModified(contentResolver, uri)
72+
val lastModified = lastModifiedResolver.resolve(uri)
7373

7474
currentTempPath = "${FileStorageUtils.getTemporalPath(user.accountName)}$remotePath"
7575
val cacheFile = File(currentTempPath)
@@ -129,18 +129,11 @@ class CopyAndUploadContentUrisTask(
129129
}
130130
}
131131

132-
private fun queryLastModified(contentResolver: ContentResolver, uri: Uri): Long = runCatching {
133-
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
134-
if (!cursor.moveToFirst()) return@use 0L
135-
val col = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
136-
if (col >= 0) cursor.getLong(col) else 0L
137-
} ?: 0L
138-
}.getOrDefault(0L)
139-
140132
private fun applyLastModified(file: File, lastModified: Long) {
141-
if (lastModified == 0L) return
142-
runCatching { file.setLastModified(lastModified) }
143-
.onFailure { Log_OC.w(TAG, "Could not set lastModified on cache file: ${it.message}") }
133+
if (lastModified <= 0L) return
134+
if (!file.setLastModified(lastModified)) {
135+
Log_OC.w(TAG, "Could not set lastModified on cache file: ${file.path}")
136+
}
144137
}
145138

146139
private fun dispatchResult(result: ResultCode) {

0 commit comments

Comments
 (0)