Fix Android file listing: replace stat with ls -la for compatibility

Samsung (and other Android vendors using toybox) do not support GNU
stat -c flag syntax. Switch to find for file discovery + ls -la per
file for size/date metadata, which works on all Android versions.
This commit is contained in:
Kyle Bolen
2026-07-28 04:36:40 +00:00
parent 3da4e464d2
commit ec114aa7ce

View File

@@ -90,32 +90,34 @@ class AndroidConnector(
val check = runAdb(serial, "shell", "ls", dir, "2>/dev/null")
if (check.contains("No such file") || check.isBlank()) continue
val statOutput = runAdb(
// Use `find` to get filenames, then `ls -la` for size/date.
// Avoids relying on `stat` which has inconsistent flag support across
// Android versions (Samsung/toybox vs GNU coreutils).
val findOutput = runAdb(
serial, "shell",
"find", dir, "-maxdepth", "1", "-type", "f",
"-exec", "stat", "-c", "%s %Y %n", "{}", ";"
"find", dir, "-maxdepth", "1", "-type", "f"
)
for (line in statOutput.lines()) {
val trimmed = line.trim()
if (trimmed.isBlank()) continue
val parts = trimmed.split(" ", limit = 3)
if (parts.size < 3) continue
for (line in findOutput.lines()) {
val path = line.trim()
if (path.isBlank() || path.contains("No such file")) continue
val sizeBytes = parts[0].toLongOrNull() ?: continue
val epochSecs = parts[1].toLongOrNull() ?: continue
val path = parts[2]
val filename = path.substringAfterLast("/")
val ext = filename.substringAfterLast(".", "").lowercase()
if (ext !in mediaExtensions) continue
// Get size and modification time via ls -la on the specific file
val lsLine = runAdb(serial, "shell", "ls", "-la", path).trim()
// ls -la format: -rw-rw---- 1 root everybody 3821234 2026-07-15 14:22 /sdcard/...
val sizeBytes = parseLsSize(lsLine) ?: 0L
val dateTaken = parseLsDate(lsLine) ?: java.time.Instant.EPOCH
photos.add(
PhonePhoto(
devicePath = path,
filename = filename,
sizeBytes = sizeBytes,
dateTaken = Instant.ofEpochSecond(epochSecs),
dateTaken = dateTaken,
exifMake = device.displayName,
exifModel = device.displayName,
mediaType = if (ext in setOf("mp4", "mov", "3gp", "avi"))
@@ -173,6 +175,36 @@ class AndroidConnector(
private fun requireSerial(device: DeviceInfo): String =
device.adbSerial ?: throw DeviceException("No ADB serial for device: ${device.displayName}")
/**
* Parse file size from `ls -la` output line.
* Format: -rw-rw---- 1 root everybody 3821234 2026-07-15 14:22 filename
* Size is the 5th whitespace-separated token.
*/
private fun parseLsSize(lsLine: String): Long? {
val parts = lsLine.trim().split(Regex("\\s+"))
// Size is at index 4 (0-based): permissions, links, user, group, SIZE, date, time, name
return parts.getOrNull(4)?.toLongOrNull()
}
/**
* Parse modification date from `ls -la` output line.
* Date token is at index 5 (yyyy-MM-dd) and time at index 6 (HH:mm).
*/
private fun parseLsDate(lsLine: String): java.time.Instant? {
return try {
val parts = lsLine.trim().split(Regex("\\s+"))
val datePart = parts.getOrNull(5) ?: return null
val timePart = parts.getOrNull(6) ?: "00:00"
val ldt = java.time.LocalDateTime.parse(
"$datePart $timePart",
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
ldt.atZone(java.time.ZoneId.systemDefault()).toInstant()
} catch (_: Exception) {
null
}
}
private fun runAdb(serial: String?, vararg args: String): String {
val cmd = buildList {
add(adbBin)