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