Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,7 @@
## 2024-06-13 - Optimize CameraCharacteristics retrieval loop
**Learning:** When querying expensive Android system services like `CameraManager.getCameraCharacteristics` inside a loop, combining filter and map operations into a single pass (e.g., using `mapNotNull`) prevents redundant IPC calls and noticeably improves execution speed.
**Action:** Replaced a `filter {} .forEach {}` pipeline containing duplicated `getCameraCharacteristics` calls with a single `mapNotNull` pass that pairs valid camera IDs with their retrieved characteristics, eliminating 50% of the overhead.

## 2026-09-09 - Copy to clipboard toast UX
**Learning:** Native clipboard feedback was added in Android 13, but custom implementations for older versions are often forgotten. When implementing copy actions using `ClipboardManager`, you must explicitly check the API version (`Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2`) and only provide manual visual feedback (like Toasts) on older OS versions to prevent annoying duplicate overlays on newer devices.
**Action:** Always condition custom copy-to-clipboard visual feedback on the target OS version to defer to native OS behavior when available.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package com.samsung.android.scan3d.util

import android.content.ClipData
import android.content.Context
import android.os.Build
import android.widget.Toast
import com.samsung.android.scan3d.R

object ClipboardUtil {

fun copyToClipboard(context: Context?, label: String, text: String) {
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as? android.content.ClipboardManager ?: return
val clip = ClipData.newPlainText(label, text)
clipboard.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.samsung.android.scan3d.util
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.widget.Toast
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.*
Expand All @@ -15,18 +17,23 @@ class ClipboardUtilTest {
val mockContext = mock(Context::class.java)
val mockClipboard = mock(ClipboardManager::class.java)
val mockClipData = mock(ClipData::class.java)
val mockToast = mock(Toast::class.java)

`when`(mockContext.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(mockClipboard)

mockStatic(ClipData::class.java).use { mockedStatic ->
mockedStatic.`when`<ClipData> { ClipData.newPlainText("test_label", "test_text") }.thenReturn(mockClipData)
mockStatic(Toast::class.java).use { mockedToastStatic ->
mockedToastStatic.`when`<Toast> { Toast.makeText(any(Context::class.java), anyInt(), anyInt()) }.thenReturn(mockToast)

mockStatic(ClipData::class.java).use { mockedStatic ->
mockedStatic.`when`<ClipData> { ClipData.newPlainText("test_label", "test_text") }.thenReturn(mockClipData)

ClipboardUtil.copyToClipboard(mockContext, "test_label", "test_text")
ClipboardUtil.copyToClipboard(mockContext, "test_label", "test_text")

val clipCaptor = ArgumentCaptor.forClass(ClipData::class.java)
verify(mockClipboard).setPrimaryClip(clipCaptor.capture())
val clipCaptor = ArgumentCaptor.forClass(ClipData::class.java)
verify(mockClipboard).setPrimaryClip(clipCaptor.capture())

assertEquals(mockClipData, clipCaptor.value)
assertEquals(mockClipData, clipCaptor.value)
}
}
}

Expand All @@ -36,13 +43,8 @@ class ClipboardUtilTest {
mockStatic(ClipData::class.java).use { mockedStatic ->
mockedStatic.`when`<ClipData> { ClipData.newPlainText("test_label", "test_text") }.thenReturn(mockClipData)

try {
ClipboardUtil.copyToClipboard(null, "test_label", "test_text")
fail("Expected Exception when context is null")
} catch (e: Exception) {
// If context is null, it throws NullPointerException because of `as android.content.ClipboardManager`
assertEquals(NullPointerException::class.java, e::class.java)
}
// Since context is null, it returns early now due to the safe cast and null check `?: return`
ClipboardUtil.copyToClipboard(null, "test_label", "test_text")
}
}
}