From ff7fa13afc488498888435e10b92456776ec56bf Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Mon, 8 Jun 2026 20:35:21 +0000
Subject: [PATCH] Initialize interactive accessibility elements synchronously
Moved the setup of `AccessibilityDelegateCompat` and initial interaction handlers out of asynchronous coroutines to ensure screen readers immediately identify components as interactive. Added a fallback loading toast for attempts to trigger the copy-IP action before local network resolution completes.
Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com>
---
.Jules/palette.md | 4 ++
.../scan3d/fragments/CameraFragment.kt | 54 +++++++++++--------
app/src/main/res/values/strings.xml | 1 +
3 files changed, 37 insertions(+), 22 deletions(-)
diff --git a/.Jules/palette.md b/.Jules/palette.md
index dd03210..e6ca9b3 100644
--- a/.Jules/palette.md
+++ b/.Jules/palette.md
@@ -150,3 +150,7 @@
## 2026-05-30 - User-Facing System Notifications
**Learning:** Foreground service notifications and channels are exposed directly to users within their system drawer and app settings. Hardcoding technical or aggressive terminology (e.g., naming a channel ID as the visible name or using "Kill" as an action button) degrades the user experience and violates UX standards. Furthermore, failing to extract these strings to `strings.xml` prevents localization and accessibility optimizations.
**Action:** Always avoid hardcoded, technical, or aggressive terminology in user-facing system notifications and foreground service controls. Utilize standard mobile UX phrasing (e.g., "Stop", "Tap") and ensure all notification text (titles, descriptions, actions) is extracted to localized string resources.
+
+## 2026-06-08 - Async View Initialization and Accessibility
+**Learning:** Interactive elements with dynamic text and secondary actions must have their full actionable `contentDescription` and their custom `AccessibilityDelegateCompat` set during view initialization. Waiting for an asynchronous block (like a network request or local IP resolution) to complete before assigning these properties creates a window where the element is completely inaccessible to screen readers, and tapping it may result in no feedback.
+**Action:** Always provide explicit feedback (e.g., a Toast) if a user interacts with a UI element that is visibly actionable but functionally disabled due to a loading state. Furthermore, attach the custom `AccessibilityDelegateCompat` to the view immediately during initialization, ensuring screen readers understand its interactive role even during loading.
diff --git a/app/src/main/java/com/samsung/android/scan3d/fragments/CameraFragment.kt b/app/src/main/java/com/samsung/android/scan3d/fragments/CameraFragment.kt
index 8f8bfc9..fcc8edd 100644
--- a/app/src/main/java/com/samsung/android/scan3d/fragments/CameraFragment.kt
+++ b/app/src/main/java/com/samsung/android/scan3d/fragments/CameraFragment.kt
@@ -85,6 +85,38 @@ class CameraFragment : Fragment() {
): View {
_fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)
+ _fragmentCameraBinding?.let { binding ->
+ // Set initial state before async operation completes
+ binding.textView6.contentDescription = getString(R.string.actionable_content_description_format, getString(R.string.default_ip), getString(R.string.copy_ip_tooltip))
+ binding.textView6.setOnClickListener {
+ Toast.makeText(context, R.string.ip_loading_message, Toast.LENGTH_SHORT).show()
+ }
+
+ ViewCompat.setAccessibilityDelegate(binding.textView6, object : AccessibilityDelegateCompat() {
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.className = android.widget.Button::class.java.name
+ val clickAction = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK.id,
+ getString(R.string.copy_ip_tooltip)
+ )
+ info.addAction(clickAction)
+ }
+ })
+
+ ViewCompat.setAccessibilityDelegate(binding.textView2, object : AccessibilityDelegateCompat() {
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.className = android.widget.Button::class.java.name
+ val clickAction = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK.id,
+ getString(R.string.repo_desc)
+ )
+ info.addAction(clickAction)
+ }
+ })
+ }
+
// Get the local ip address
viewLifecycleOwner.lifecycleScope.launch {
val localIp = IpUtil.getLocalIpAddress()
@@ -102,29 +134,7 @@ class CameraFragment : Fragment() {
}
}
- ViewCompat.setAccessibilityDelegate(binding.textView6, object : AccessibilityDelegateCompat() {
- override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
- super.onInitializeAccessibilityNodeInfo(host, info)
- info.className = android.widget.Button::class.java.name
- val clickAction = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK.id,
- getString(R.string.copy_ip_tooltip)
- )
- info.addAction(clickAction)
- }
- })
- ViewCompat.setAccessibilityDelegate(binding.textView2, object : AccessibilityDelegateCompat() {
- override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
- super.onInitializeAccessibilityNodeInfo(host, info)
- info.className = android.widget.Button::class.java.name
- val clickAction = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK.id,
- getString(R.string.repo_desc)
- )
- info.addAction(clickAction)
- }
- })
binding.textView2.setOnClickListener {
val url = getString(R.string.repo_url)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index abdc15c..339135d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -76,4 +76,5 @@
%1$d ms
%1$d kB/sec
%1$d%%
+ Loading IP address, please wait…