From 04b5b7f6aa469a368bcf8f1d75046770d3f83d63 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 19:02:33 +0000 Subject: Fix bugs from static analysis; add test coverage for new code Bugs fixed: - LogbookStorage.copyFromUri: return null when ContentResolver returns null stream instead of returning a path to an unwritten file - VoiceLogFragment adapter: decode photos as downsampled thumbnails (128x96 target) using BitmapFactory inSampleSize instead of loading full-resolution bitmaps on the main thread Tests added: - LogbookRepositoryTest: ID sequencing, insertion order, storage delegation, reload from persisted state, lat/lon preservation - TripReportGeneratorTest: log entry time-range filtering, distance calculation, empty-points edge case, all narrative styles smoke-tested https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn --- .../src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt | 5 ++--- .../kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt | 13 ++++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'android-app/app/src/main') diff --git a/android-app/app/src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt b/android-app/app/src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt index 35349f0..6b90542 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt @@ -44,9 +44,8 @@ class LogbookStorage(private val context: Context) { fun copyFromUri(uri: Uri): String? = runCatching { val dest = File(photoDir, "log_${System.currentTimeMillis()}.jpg") - context.contentResolver.openInputStream(uri)?.use { input -> - FileOutputStream(dest).use { output -> input.copyTo(output) } - } + val stream = context.contentResolver.openInputStream(uri) ?: return null + stream.use { input -> FileOutputStream(dest).use { output -> input.copyTo(output) } } dest.absolutePath }.getOrElse { null } } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt index 31d9715..3638f8f 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt @@ -368,6 +368,16 @@ class VoiceLogFragment : Fragment() { } } +// ── Helpers ──────────────────────────────────────────────────────────────────── + +private fun decodeThumbnail(path: String, targetW: Int, targetH: Int): android.graphics.Bitmap? { + val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeFile(path, opts) + if (opts.outWidth <= 0) return null + val scale = maxOf(opts.outWidth / targetW, opts.outHeight / targetH).coerceAtLeast(1) + return BitmapFactory.decodeFile(path, BitmapFactory.Options().apply { inSampleSize = scale }) +} + // ── Logbook entries adapter ──────────────────────────────────────────────────── private val TIME_FMT: DateTimeFormatter = @@ -409,11 +419,12 @@ private class LogbookEntryAdapter : RecyclerView.Adapter val photoPath = e.photoPath if (photoPath != null && File(photoPath).exists()) { - val bm = BitmapFactory.decodeFile(photoPath) + val bm = decodeThumbnail(photoPath, 128, 96) if (bm != null) { holder.ivPhoto.setImageBitmap(bm) holder.ivPhoto.visibility = View.VISIBLE } else { + holder.ivPhoto.setImageDrawable(null) holder.ivPhoto.visibility = View.GONE } } else { -- cgit v1.2.3