diff options
| author | Claude <noreply@anthropic.com> | 2026-05-25 19:02:33 +0000 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-05-25 19:02:33 +0000 |
| commit | 04b5b7f6aa469a368bcf8f1d75046770d3f83d63 (patch) | |
| tree | 42bd875500262561e3402cc7e2cadb61452ea221 /android-app/app/src/main | |
| parent | 620aa6c7419a611639be5f005c34a27e19bfee89 (diff) | |
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
Diffstat (limited to 'android-app/app/src/main')
| -rw-r--r-- | android-app/app/src/main/kotlin/org/terst/nav/logbook/LogbookStorage.kt | 5 | ||||
| -rw-r--r-- | android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt | 13 |
2 files changed, 14 insertions, 4 deletions
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<LogbookEntryAdapter.VH> 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 { |
