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…