diff --git a/lint-baseline.xml b/lint-baseline.xml
index b8c648e41..2c52a5c5e 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -10652,105 +10652,6 @@
column="12"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{
- // Speech of an item that touch focuses still interrupts what was being said.
+ // Without this, speech stops when touch focuses an item instead, below.
return touchStopsSpeech
? Feedback.create(eventId, Feedback.part().setInterruptGentle(true).build())
: null;
}
case TOUCH_FOCUSED_NODE -> {
- return toFeedback(
+ return toTouchFocusFeedback(
eventId, Feedback.focus(FOCUS_FOR_TOUCH).setTarget(touchTarget).setForceRefocus(true));
}
case TOUCH_UNFOCUSED_NODE -> {
- return toFeedback(eventId, Feedback.focus(FOCUS_FOR_TOUCH).setTarget(touchTarget));
+ return toTouchFocusFeedback(eventId, Feedback.focus(FOCUS_FOR_TOUCH).setTarget(touchTarget));
}
case LIFT -> {
return toFeedback(eventId, Feedback.focus(CLICK_NODE).setTarget(touchTarget));
@@ -154,6 +154,17 @@ private static Feedback toFeedback(@Nullable EventId eventId, Focus.Builder focu
return Feedback.create(eventId, Feedback.part().setFocus(focus.build()).build());
}
+ /**
+ * Focuses an item by touch. When touching doesn't stop speech, focusing the item does, as touching
+ * would have: notifications, toasts and other speech that new speech can't interrupt would
+ * otherwise keep the item from being spoken until they finish.
+ */
+ private static Feedback toTouchFocusFeedback(@Nullable EventId eventId, Focus.Builder focus) {
+ return Feedback.create(
+ eventId,
+ Feedback.part().setInterruptGentle(!touchStopsSpeech).setFocus(focus.build()).build());
+ }
+
/** Feedback-mapping function. */
public static Part.@Nullable Builder onNodeManuallyScrolled(
Variables variables, int depth, FocusFinder focusFinder) {
diff --git a/talkback/src/test/java/com/google/android/accessibility/talkback/focusmanagement/FocusFeedbackMapperTouchTest.kt b/talkback/src/test/java/com/google/android/accessibility/talkback/focusmanagement/FocusFeedbackMapperTouchTest.kt
new file mode 100644
index 000000000..207c1708c
--- /dev/null
+++ b/talkback/src/test/java/com/google/android/accessibility/talkback/focusmanagement/FocusFeedbackMapperTouchTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2026 Backtalk contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.accessibility.talkback.focusmanagement
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.google.android.accessibility.talkback.Feedback
+import com.google.android.accessibility.talkback.Interpretation
+import com.google.android.accessibility.talkback.Interpretation.Touch.Action
+import com.google.android.accessibility.talkback.Mappers
+import com.google.android.accessibility.utils.Performance.EVENT_ID_UNTRACKED
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [34])
+class FocusFeedbackMapperTouchTest {
+ private val node = AccessibilityNodeInfoCompat.obtain()
+
+ @After
+ fun restoreDefault() {
+ FocusFeedbackMapper.setTouchStopsSpeech(true)
+ }
+
+ @Test
+ fun touchStopsSpeech_stopsItOnTouch_notOnFocus() {
+ FocusFeedbackMapper.setTouchStopsSpeech(true)
+ assertTrue(part(Action.TOUCH_START)!!.interruptGentle())
+ assertTrue(part(Action.TOUCH_ENTERED_UNFOCUSED_NODE)!!.interruptGentle())
+ assertFalse(part(Action.TOUCH_UNFOCUSED_NODE)!!.interruptGentle())
+ assertFalse(part(Action.TOUCH_FOCUSED_NODE)!!.interruptGentle())
+ }
+
+ @Test
+ fun touchDoesNotStopSpeech_stopsItWhenTouchFocusesAnItem() {
+ FocusFeedbackMapper.setTouchStopsSpeech(false)
+ assertNull(part(Action.TOUCH_START))
+ assertNull(part(Action.TOUCH_ENTERED_UNFOCUSED_NODE))
+ // Speech that new speech can't interrupt, such as a notification, would otherwise keep the
+ // item from being spoken.
+ val focus = part(Action.TOUCH_UNFOCUSED_NODE)!!
+ assertTrue(focus.interruptGentle())
+ assertNotNull(focus.focus())
+ assertTrue(part(Action.TOUCH_FOCUSED_NODE)!!.interruptGentle())
+ }
+
+ private fun part(action: Action): Feedback.Part? {
+ val variables =
+ Mappers.Variables(
+ RuntimeEnvironment.getApplication(),
+ /* event= */ null,
+ Interpretation.Touch.create(action, node),
+ /* monitors= */ null,
+ )
+ return FocusFeedbackMapper.mapTouchToFocusAction(EVENT_ID_UNTRACKED, variables, 0)
+ ?.failovers()
+ ?.first()
+ }
+}
diff --git a/talkback/src/wear/res/values-watch-v30/colors.xml b/talkback/src/wear/res/values-watch-v30/colors.xml
deleted file mode 100644
index 5b2415903..000000000
--- a/talkback/src/wear/res/values-watch-v30/colors.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
- @android:color/system_neutral1_900
- @android:color/system_neutral1_50
- @android:color/system_accent1_600
- @android:color/system_neutral2_700
- @android:color/system_neutral2_200
- @android:color/system_neutral2_200
- @android:color/system_neutral1_10
- @android:color/system_neutral1_800
- @android:color/system_neutral1_800
- @color/google_black
- @color/google_white
-
diff --git a/utils/src/main/res/values-v31/colors.xml b/utils/src/main/res/values-v31/colors.xml
index 34008b0b9..a6fde95be 100644
--- a/utils/src/main/res/values-v31/colors.xml
+++ b/utils/src/main/res/values-v31/colors.xml
@@ -15,5 +15,14 @@
limitations under the License.
-->
-
+
+ @android:color/system_neutral1_900
+ @android:color/system_neutral1_50
+ @android:color/system_accent1_600
+ @android:color/system_neutral2_700
+ @android:color/system_neutral2_200
+ @android:color/system_neutral2_200
+ @android:color/system_neutral1_10
+ @android:color/system_neutral1_800
+ @android:color/system_neutral1_800
diff --git a/utils/src/main/res/values/colors.xml b/utils/src/main/res/values/colors.xml
index be70f4530..55d460702 100644
--- a/utils/src/main/res/values/colors.xml
+++ b/utils/src/main/res/values/colors.xml
@@ -43,16 +43,17 @@
@color/google_black
-
- @android:color/system_neutral1_900
- @android:color/system_neutral1_50
- @android:color/system_accent1_600
- @android:color/system_neutral2_700
- @android:color/system_neutral2_200
- @android:color/system_neutral2_200
- @android:color/system_neutral1_10
- @android:color/system_neutral1_800
- @android:color/system_neutral1_800
+
+ @color/google_grey900
+ @color/google_grey50
+ @color/google_blue600
+ @color/google_grey700
+ @color/google_grey200
+ @color/google_grey200
+ @color/google_grey50
+ @color/google_grey800
+ @color/google_grey800
@color/google_black
@color/google_white