diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt index e322c79..c3e6db6 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/BrowseScreen.kt @@ -365,23 +365,32 @@ private fun PhotoPreviewDialog( LaunchedEffect(photo.devicePath) { val bytes = withContext(Dispatchers.IO) { - // Use cached thumbnail if available + // Always use the cached JPEG (sips-converted) — works for HEIC too if (cachePath.toFile().exists() && cachePath.toFile().length() > 0) { java.nio.file.Files.readAllBytes(cachePath) } else { - // Pull fresh for preview + // Pull to temp, convert to JPEG via sips, cache it val tmpDir = Paths.get(System.getProperty("java.io.tmpdir"), "photophetch-preview") tmpDir.toFile().mkdirs() val tmpFile = tmpDir.resolve("preview-${photo.filename}") val ok = pullFn(photo.devicePath, tmpFile) - if (ok && tmpFile.toFile().exists()) java.nio.file.Files.readAllBytes(tmpFile) else null + if (ok && tmpFile.toFile().exists()) { + val sips = ProcessBuilder("sips", "--resampleWidth", "800", + "--setProperty", "format", "jpeg", + tmpFile.toString(), "--out", cachePath.toString()) + .redirectErrorStream(true).start() + sips.inputStream.readBytes() + sips.waitFor() + tmpFile.toFile().delete() + if (cachePath.toFile().exists()) java.nio.file.Files.readAllBytes(cachePath) else null + } else null } } if (bytes != null) { runCatching { bitmap = SkiaImage.makeFromEncoded(bytes).toComposeImageBitmap() } } - // For Live Photos: also open the companion MOV in system viewer + // For Live Photos: open the companion MOV in system viewer if (photo.isLivePhotoStill && photo.companionDevicePath != null) { val tmpDir = Paths.get(System.getProperty("java.io.tmpdir"), "photophetch-preview") val movFilename = photo.companionDevicePath.substringAfterLast("/") diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt index af07aac..d5e2dfc 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt @@ -310,7 +310,7 @@ private fun VerifyResultRow( Text( when { isFailed -> result.error ?: "Failed" - exifUnverified -> "⚠ Make: '${result.exifMake?.ifEmpty { "absent" } ?: "absent"}'" + + exifUnverified -> "⚠ Origin unknown" + if (showCheckbox) " — kept on phone" else "" exifUnavailable -> "${result.photo.sizeBytes / 1024} KB" else -> "✓ ${result.exifMake} · ${result.photo.sizeBytes / 1024} KB" @@ -381,8 +381,8 @@ private fun ResultPreviewDialog(result: CopyResult, onDismiss: () -> Unit) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Text(result.photo.filename, fontWeight = FontWeight.SemiBold) Text(when (result.exifVerified) { - true -> "✓ EXIF Make: ${result.exifMake}" - false -> "⚠ Make: '${result.exifMake?.ifEmpty { "(absent)" } ?: "(absent)"}' — not a confirmed camera original" + true -> "✓ Camera original — Make: ${result.exifMake}" + false -> "⚠ Origin unknown — may not be your photo${if (!result.exifMake.isNullOrBlank()) " (Make: ${result.exifMake})" else ""}" null -> "EXIF not checked (install exiftool)" }, style = MaterialTheme.typography.bodySmall, color = when (result.exifVerified) {