From 53207c636e21310be2557545b75940ec67af6648 Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Thu, 19 Mar 2020 01:05:14 +0300 Subject: [PATCH 1/7] Voice markup --- compositor/src/main/java/Compositor.java | 63 +++++++++++++++++++ compositor/src/main/res/raw/compositor.json | 53 +++++++++++++++- talkback/src/main/java/TalkBackService.java | 30 +++++++++ talkback/src/main/res/values-ru/strings.xml | 17 +++++ .../src/main/res/values/donottranslate.xml | 27 ++++++++ talkback/src/main/res/values/strings.xml | 16 +++++ .../main/res/xml/verbosity_preferences.xml | 35 +++++++++++ 7 files changed, 240 insertions(+), 1 deletion(-) diff --git a/compositor/src/main/java/Compositor.java b/compositor/src/main/java/Compositor.java index 687daa1c2..16de44e41 100644 --- a/compositor/src/main/java/Compositor.java +++ b/compositor/src/main/java/Compositor.java @@ -206,6 +206,8 @@ public class Compositor { static final int ENUM_RANGE_INFO_TYPE = 6; static final int RANGE_INFO_UNDEFINED = -1; + private static final int ENUM_VOICE_TYPE = 7; + // Enum values private static final int QUEUE_MODE_INTERRUPTIBLE_IF_LONG = 0x40000001; @@ -225,6 +227,23 @@ public class Compositor { public static final int DESC_ORDER_STATE_NAME_ROLE_POSITION = 1; public static final int DESC_ORDER_NAME_ROLE_STATE_POSITION = 2; + /** Voice type ids. */ + @IntDef({ + VOICE_TYPE_LOW, + VOICE_TYPE_REDUCED, + VOICE_TYPE_NORMAL, + VOICE_TYPE_ELEVATED, + VOICE_TYPE_HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VoiceType {} + + public static final int VOICE_TYPE_LOW = 0; + public static final int VOICE_TYPE_REDUCED = 1; + public static final int VOICE_TYPE_NORMAL = 2; + public static final int VOICE_TYPE_ELEVATED = 3; + public static final int VOICE_TYPE_HIGH = 4; + ///////////////////////////////////////////////////////////////////////////////// // Member variables @@ -257,6 +276,9 @@ static class Constants { boolean mSpeakRoles = true; boolean mSpeakCollectionInfo = true; @DescriptionOrder int mDescriptionOrder = DESC_ORDER_ROLE_NAME_STATE_POSITION; + @VoiceType int mActionableElementVoice = VOICE_TYPE_NORMAL; + @VoiceType int mButtonVoice = VOICE_TYPE_NORMAL; + @VoiceType int mImageVoice = VOICE_TYPE_NORMAL; boolean mSpeakElementIds = false; } @@ -325,6 +347,27 @@ public void setDescriptionOrder(@DescriptionOrder int descOrderInt) { } } + public void setActionableElementVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mActionableElementVoice) { + mConstants.mActionableElementVoice = voiceType; + mParseTreeIsStale = true; + } + } + + public void setButtonVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mButtonVoice) { + mConstants.mButtonVoice = voiceType; + mParseTreeIsStale = true; + } + } + + public void setImageVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mImageVoice) { + mConstants.mImageVoice = voiceType; + mParseTreeIsStale = true; + } + } + public void setSpeakElementIds(boolean speakElementIds) { if (speakElementIds != mConstants.mSpeakElementIds) { mConstants.mSpeakElementIds = speakElementIds; @@ -683,6 +726,18 @@ private static void declareConstants(ParseTree parseTree, Constants constants) { "VERBOSITY_DESCRIPTION_ORDER", ENUM_VERBOSITY_DESCRIPTION_ORDER, constants.mDescriptionOrder); + parseTree.setConstantEnum( + "ACTIONABLE_ELEMENT_VOICE", + ENUM_VOICE_TYPE, + constants.mActionableElementVoice); + parseTree.setConstantEnum( + "BUTTON_VOICE", + ENUM_VOICE_TYPE, + constants.mButtonVoice); + parseTree.setConstantEnum( + "IMAGE_VOICE", + ENUM_VOICE_TYPE, + constants.mImageVoice); parseTree.setConstantBool("VERBOSITY_SPEAK_ELEMENT_IDS", constants.mSpeakElementIds); } @@ -773,6 +828,14 @@ private static void declareEnums(ParseTree parseTree) { verbosityDescOrderValues.put(DESC_ORDER_NAME_ROLE_STATE_POSITION, "NameRoleStatePosition"); parseTree.addEnum(ENUM_VERBOSITY_DESCRIPTION_ORDER, verbosityDescOrderValues); + Map voiceTypeValues = new HashMap<>(); + voiceTypeValues.put(VOICE_TYPE_LOW, "low"); + voiceTypeValues.put(VOICE_TYPE_REDUCED, "reduced"); + voiceTypeValues.put(VOICE_TYPE_NORMAL, "normal"); + voiceTypeValues.put(VOICE_TYPE_ELEVATED, "elevated"); + voiceTypeValues.put(VOICE_TYPE_HIGH, "high"); + parseTree.addEnum(ENUM_VOICE_TYPE, voiceTypeValues); + Map rangeInfoTypes = new HashMap<>(); rangeInfoTypes.put(RangeInfo.RANGE_TYPE_INT, "int"); rangeInfoTypes.put(RangeInfo.RANGE_TYPE_FLOAT, "float"); diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index f8ea23c73..fc0fa3c97 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -209,6 +209,7 @@ "then":"queue", "else":"flush" }, + "ttsPitch": "%get_voice_for_element", "ttsAddToHistory": true, // TODO: Remove the next line when focus management feature is settled down. //"ttsForceFeedback": "!$global.syncedAccessibilityFocusLatch && $node.role != 'web_view'", @@ -242,6 +243,7 @@ }, "TYPE_VIEW_FOCUSED": { "ttsOutput": "%event_description", + "ttsPitch": "%get_voice_for_element", "ttsAddToHistory": true, // From FallbackFormatter // TODO: Delete porting comments. @@ -256,6 +258,7 @@ }, "TYPE_VIEW_HOVER_ENTER": { "ttsOutput": "%event_description", + "ttsPitch": "%get_voice_for_element", "ttsAddToHistory": true, // TODO: respect user settings "ttsForceFeedbackAudioPlaybackActive": true, @@ -1799,6 +1802,54 @@ } } ] - } + }, + "get_voice_for_element": { + "switch": "$node.role", + "cases": { + "button": "%button_voice", + "image_button": "%button_voice", + "image": "%image_voice" + }, + "default": { + "if": "$node.isActionable", + "then": "%actionable_element_voice", + "else": "%voice_normal" + } + }, + "actionable_element_voice": { + "switch": "#ACTIONABLE_ELEMENT_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, + "button_voice": { + "switch": "#BUTTON_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, + "image_voice": { + "switch": "#IMAGE_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, + "voice_low": 0.6, + "voice_reduced": 0.8, + "voice_normal": 1.0, + "voice_elevated": 1.25, + "voice_high": 1.5 } } diff --git a/talkback/src/main/java/TalkBackService.java b/talkback/src/main/java/TalkBackService.java index 7db2ce877..650c73b25 100644 --- a/talkback/src/main/java/TalkBackService.java +++ b/talkback/src/main/java/TalkBackService.java @@ -1878,6 +1878,18 @@ private void reloadPreferences() { prefs, res, R.string.pref_node_desc_order_key, R.string.pref_node_desc_order_default); compositor.setDescriptionOrder(prefValueToDescriptionOrder(res, descriptionOrder)); + // Update voice markup preferences. + String voiceType = + SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_button_voice_key, R.string.pref_voice_default); + compositor.setButtonVoice(prefValueToVoiceType(res, voiceType)); + voiceType = SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_image_voice_key, R.string.pref_voice_default); + compositor.setImageVoice(prefValueToVoiceType(res, voiceType)); + voiceType = SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_actionable_voice_key, R.string.pref_voice_default); + compositor.setActionableElementVoice(prefValueToVoiceType(res, voiceType)); + // Update preference: speak element IDs. boolean speakElementIds = SharedPreferencesUtils.getBooleanPref( @@ -1918,6 +1930,24 @@ private void reloadPreferences() { } } + private static @Compositor.VoiceType int prefValueToVoiceType( + Resources resources, String value) { + if (TextUtils.equals( + value, resources.getString(R.string.voice_type_low_pitch))) { + return Compositor.VOICE_TYPE_LOW; + } else if (TextUtils.equals( + value, resources.getString(R.string.voice_type_reduced_pitch))) { + return Compositor.VOICE_TYPE_REDUCED; + } else if (TextUtils.equals( + value, resources.getString(R.string.voice_type_elevated_pitch))) { + return Compositor.VOICE_TYPE_ELEVATED; + } else if (TextUtils.equals( + value, resources.getString(R.string.voice_type_high_pitch))) { + return Compositor.VOICE_TYPE_HIGH; + } + return Compositor.VOICE_TYPE_NORMAL; + } + /** * Attempts to return the state of touch exploration. * diff --git a/talkback/src/main/res/values-ru/strings.xml b/talkback/src/main/res/values-ru/strings.xml index 8d6ea2dac..8be0daf47 100644 --- a/talkback/src/main/res/values-ru/strings.xml +++ b/talkback/src/main/res/values-ru/strings.xml @@ -523,4 +523,21 @@ Введите поисковый запрос TalkBack Канал уведомлений TalkBack + + + Низкий + Пониженный + Нормальный + Повышенный + Высокий + + + Голоса для различных элементов + Голос для озвучивания кнопок + Кнопки + Голос для озвучивания изображений + Изображения + Голос для озвучивания активных элементов + Активные элементы + diff --git a/talkback/src/main/res/values/donottranslate.xml b/talkback/src/main/res/values/donottranslate.xml index d902eeafc..6abd675c5 100644 --- a/talkback/src/main/res/values/donottranslate.xml +++ b/talkback/src/main/res/values/donottranslate.xml @@ -585,4 +585,31 @@ pref_key_last_log_time_key + + + @string/low_pitch_voice + @string/reduced_pitch_voice + @string/normal_voice + @string/elevated_pitch_voice + @string/high_pitch_voice + + + @string/voice_type_low_pitch + @string/voice_type_reduced_pitch + @string/voice_type_normal + @string/voice_type_elevated_pitch + @string/voice_type_high_pitch + + low_pitch + reduced_pitch + normal + elevated_pitch + high_pitch + @string/voice_type_normal + + + actionable_voice_pitch + image_voice_pitch + button_voice_pitch + diff --git a/talkback/src/main/res/values/strings.xml b/talkback/src/main/res/values/strings.xml index 95c21e045..9e96778b7 100644 --- a/talkback/src/main/res/values/strings.xml +++ b/talkback/src/main/res/values/strings.xml @@ -1518,4 +1518,20 @@ The Notification Channel of TalkBack + + Low pitch + Reduced pitch + Normal + Elevated pitch + High pitch + + + Voices for various elements + Voice for speaking buttons + Buttons + Voice for speaking images + Images + Voice for speaking actionable elements + Actionable elements + diff --git a/talkback/src/main/res/xml/verbosity_preferences.xml b/talkback/src/main/res/xml/verbosity_preferences.xml index 73a7dfeca..731dc53b7 100644 --- a/talkback/src/main/res/xml/verbosity_preferences.xml +++ b/talkback/src/main/res/xml/verbosity_preferences.xml @@ -84,6 +84,41 @@ + + + + + + + + + + + + + + From 963ba33ae3b2214a137a1e8222635e532e72eed5 Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Sun, 22 Mar 2020 02:32:47 +0300 Subject: [PATCH 2/7] Voice markup enhancement More element types distinguishable by voice. --- compositor/src/main/java/Compositor.java | 24 ++++++++++ compositor/src/main/res/raw/compositor.json | 46 +++++++++++++++---- talkback/src/main/java/TalkBackService.java | 6 +++ talkback/src/main/res/values-ru/strings.xml | 8 +++- .../src/main/res/values/donottranslate.xml | 2 + talkback/src/main/res/values/strings.xml | 8 +++- .../main/res/xml/verbosity_preferences.xml | 30 ++++++++++-- 7 files changed, 106 insertions(+), 18 deletions(-) diff --git a/compositor/src/main/java/Compositor.java b/compositor/src/main/java/Compositor.java index 16de44e41..d451a51d3 100644 --- a/compositor/src/main/java/Compositor.java +++ b/compositor/src/main/java/Compositor.java @@ -278,6 +278,8 @@ static class Constants { @DescriptionOrder int mDescriptionOrder = DESC_ORDER_ROLE_NAME_STATE_POSITION; @VoiceType int mActionableElementVoice = VOICE_TYPE_NORMAL; @VoiceType int mButtonVoice = VOICE_TYPE_NORMAL; + @VoiceType int mSwitchVoice = VOICE_TYPE_NORMAL; + @VoiceType int mSelectedItemVoice = VOICE_TYPE_NORMAL; @VoiceType int mImageVoice = VOICE_TYPE_NORMAL; boolean mSpeakElementIds = false; } @@ -361,6 +363,20 @@ public void setButtonVoice(@VoiceType int voiceType) { } } + public void setSwitchVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mSwitchVoice) { + mConstants.mSwitchVoice = voiceType; + mParseTreeIsStale = true; + } + } + + public void setSelectedItemVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mSelectedItemVoice) { + mConstants.mSelectedItemVoice = voiceType; + mParseTreeIsStale = true; + } + } + public void setImageVoice(@VoiceType int voiceType) { if (voiceType != mConstants.mImageVoice) { mConstants.mImageVoice = voiceType; @@ -734,6 +750,14 @@ private static void declareConstants(ParseTree parseTree, Constants constants) { "BUTTON_VOICE", ENUM_VOICE_TYPE, constants.mButtonVoice); + parseTree.setConstantEnum( + "SWITCH_VOICE", + ENUM_VOICE_TYPE, + constants.mSwitchVoice); + parseTree.setConstantEnum( + "SELECTED_ITEM_VOICE", + ENUM_VOICE_TYPE, + constants.mSelectedItemVoice); parseTree.setConstantEnum( "IMAGE_VOICE", ENUM_VOICE_TYPE, diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index fc0fa3c97..49a05e9dc 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -1804,16 +1804,24 @@ ] }, "get_voice_for_element": { - "switch": "$node.role", - "cases": { - "button": "%button_voice", - "image_button": "%button_voice", - "image": "%image_voice" - }, - "default": { - "if": "$node.isActionable", + "if": "$node.isSelected || $node.isChecked", + "then": "%selected_item_voice", + "else": { + "if": "$node.isWebContainer && $node.isActionable", "then": "%actionable_element_voice", - "else": "%voice_normal" + "else": { + "switch": "$node.role", + "cases": { + "button": "%button_voice", + "image_button": "%button_voice", + "radio_button": "%switch_voice", + "toggle_button": "%switch_voice", + "switch": "%switch_voice", + "check_box": "%switch_voice", + "image": "%image_voice" + }, + "default": "%voice_normal" + } } }, "actionable_element_voice": { @@ -1836,6 +1844,26 @@ "high": "%voice_high" } }, + "switch_voice": { + "switch": "#SWITCH_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, + "selected_item_voice": { + "switch": "#SELECTED_ITEM_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, "image_voice": { "switch": "#IMAGE_VOICE", "cases": { diff --git a/talkback/src/main/java/TalkBackService.java b/talkback/src/main/java/TalkBackService.java index 650c73b25..d77e068ca 100644 --- a/talkback/src/main/java/TalkBackService.java +++ b/talkback/src/main/java/TalkBackService.java @@ -1883,6 +1883,12 @@ private void reloadPreferences() { SharedPreferencesUtils.getStringPref( prefs, res, R.string.pref_button_voice_key, R.string.pref_voice_default); compositor.setButtonVoice(prefValueToVoiceType(res, voiceType)); + voiceType = SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_switch_voice_key, R.string.pref_voice_default); + compositor.setSwitchVoice(prefValueToVoiceType(res, voiceType)); + voiceType = SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_selected_item_voice_key, R.string.pref_voice_default); + compositor.setSelectedItemVoice(prefValueToVoiceType(res, voiceType)); voiceType = SharedPreferencesUtils.getStringPref( prefs, res, R.string.pref_image_voice_key, R.string.pref_voice_default); compositor.setImageVoice(prefValueToVoiceType(res, voiceType)); diff --git a/talkback/src/main/res/values-ru/strings.xml b/talkback/src/main/res/values-ru/strings.xml index 8be0daf47..d99069463 100644 --- a/talkback/src/main/res/values-ru/strings.xml +++ b/talkback/src/main/res/values-ru/strings.xml @@ -537,7 +537,11 @@ Кнопки Голос для озвучивания изображений Изображения - Голос для озвучивания активных элементов - Активные элементы + Голос для озвучивания активных элементов на web-страницах + Активные элементы на web-страницах + Голос для озвучивания флажков и переключателей + Флажки и переключатели + Голос для озвучивания выбранных элементов + Выбранные элементы diff --git a/talkback/src/main/res/values/donottranslate.xml b/talkback/src/main/res/values/donottranslate.xml index 6abd675c5..f7362b71a 100644 --- a/talkback/src/main/res/values/donottranslate.xml +++ b/talkback/src/main/res/values/donottranslate.xml @@ -611,5 +611,7 @@ actionable_voice_pitch image_voice_pitch button_voice_pitch + switch_voice_pitch + selected_item_voice_pitch diff --git a/talkback/src/main/res/values/strings.xml b/talkback/src/main/res/values/strings.xml index 9e96778b7..d7ac3e762 100644 --- a/talkback/src/main/res/values/strings.xml +++ b/talkback/src/main/res/values/strings.xml @@ -1531,7 +1531,11 @@ Buttons Voice for speaking images Images - Voice for speaking actionable elements - Actionable elements + Voice for speaking actionable elements on web pages + Actionable elements on web pages + Voice for speaking switches and checkboxes + Switches and checkboxes + Voice for speaking selected or checked items + Selected or checked items diff --git a/talkback/src/main/res/xml/verbosity_preferences.xml b/talkback/src/main/res/xml/verbosity_preferences.xml index 731dc53b7..ab775d3a8 100644 --- a/talkback/src/main/res/xml/verbosity_preferences.xml +++ b/talkback/src/main/res/xml/verbosity_preferences.xml @@ -97,17 +97,27 @@ android:summary="%s" android:title="@string/pref_button_voice_title" /> - + + android:title="@string/pref_switch_voice_title" /> + + + - + + + + From 1f2e738d364d6a035190e6a58ae9727a5a813c60 Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Tue, 24 Mar 2020 20:29:32 +0300 Subject: [PATCH 3/7] Special case of decorated checkable elements --- compositor/src/main/java/NodeVariables.java | 28 +++++++++++++++++++++ compositor/src/main/res/raw/compositor.json | 8 ++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/compositor/src/main/java/NodeVariables.java b/compositor/src/main/java/NodeVariables.java index dfb9462ad..c5d9cb6c1 100644 --- a/compositor/src/main/java/NodeVariables.java +++ b/compositor/src/main/java/NodeVariables.java @@ -28,6 +28,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.google.android.accessibility.utils.AccessibilityNodeInfoUtils; +import com.google.android.accessibility.utils.Filter; import com.google.android.accessibility.utils.LocaleUtils; import com.google.android.accessibility.utils.PackageManagerUtils; import com.google.android.accessibility.utils.Role; @@ -115,6 +116,9 @@ class NodeVariables implements ParseTree.VariableDelegate { private static final int NODE_SELF_MENU_ACTION_TYPE = 7050; private static final int NODE_IS_WEB_CONTAINER = 7051; + private static final int NODE_CONTAINS_CHECKABLE = 7052; + private static final int NODE_IS_CHECKED_INTERNALLY = 7053; + private final Context mContext; private final @Nullable LabelManager mLabelManager; private final ParseTree.VariableDelegate mParentVariables; @@ -324,6 +328,10 @@ public boolean getBoolean(int variableId) { return nodeMenuProvider != null && !nodeMenuProvider.getSelfNodeMenuActionTypes(mNode).isEmpty(); } + case NODE_CONTAINS_CHECKABLE: + return getInternalCheckable() != null; + case NODE_IS_CHECKED_INTERNALLY: + return isCheckedInternally(); default: return mParentVariables.getBoolean(variableId); } @@ -681,6 +689,24 @@ private void createVisitedNodes() { } } + private @Nullable AccessibilityNodeInfoCompat getInternalCheckable() { + List checkables = + AccessibilityNodeInfoUtils.getMatchingDescendantsOrRoot(mNode, new Filter() { + @Override + public boolean accept(AccessibilityNodeInfoCompat node) { + return node != null && node.isCheckable(); + } + }); + if (checkables != null && checkables.size() == 1) + return checkables.get(0); + return null; + } + + private boolean isCheckedInternally() { + AccessibilityNodeInfoCompat node = getInternalCheckable(); + return node != null && node.isChecked(); + } + static void declareVariables(ParseTree parseTree) { // Variables. // Nodes. @@ -741,5 +767,7 @@ static void declareVariables(ParseTree parseTree) { "node.selfMenuActionAvailable", NODE_SELF_MENU_ACTION_IS_AVAILABLE); parseTree.addStringVariable("node.selfMenuActions", NODE_SELF_MENU_ACTION_TYPE); parseTree.addBooleanVariable("node.isWebContainer", NODE_IS_WEB_CONTAINER); + parseTree.addBooleanVariable("node.containsCheckable", NODE_CONTAINS_CHECKABLE); + parseTree.addBooleanVariable("node.isCheckedInternally", NODE_IS_CHECKED_INTERNALLY); } } diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index 49a05e9dc..5f3b4274d 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -1804,7 +1804,7 @@ ] }, "get_voice_for_element": { - "if": "$node.isSelected || $node.isChecked", + "if": "$node.isSelected || $node.isChecked || $node.isCheckedInternally", "then": "%selected_item_voice", "else": { "if": "$node.isWebContainer && $node.isActionable", @@ -1820,7 +1820,11 @@ "check_box": "%switch_voice", "image": "%image_voice" }, - "default": "%voice_normal" + "default": { + "if": "$node.containsCheckable", + "then": "%switch_voice", + "else": "%voice_normal" + } } } }, From afcfa5b4011e66f268110fd5a3bb6e688c7e949a Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Wed, 25 Mar 2020 20:28:30 +0300 Subject: [PATCH 4/7] Better fit for narrow screens --- talkback/src/main/res/values-ru/strings.xml | 8 ++++---- talkback/src/main/res/values/strings.xml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/talkback/src/main/res/values-ru/strings.xml b/talkback/src/main/res/values-ru/strings.xml index d99069463..169527af1 100644 --- a/talkback/src/main/res/values-ru/strings.xml +++ b/talkback/src/main/res/values-ru/strings.xml @@ -537,11 +537,11 @@ Кнопки Голос для озвучивания изображений Изображения - Голос для озвучивания активных элементов на web-страницах - Активные элементы на web-страницах - Голос для озвучивания флажков и переключателей + Голос для озвучивания\nактивных web элементов + Активные web элементы + Голос для озвучивания\nфлажков и переключателей Флажки и переключатели - Голос для озвучивания выбранных элементов + Голос для озвучивания\nвыбранных элементов Выбранные элементы diff --git a/talkback/src/main/res/values/strings.xml b/talkback/src/main/res/values/strings.xml index d7ac3e762..e522ae0f4 100644 --- a/talkback/src/main/res/values/strings.xml +++ b/talkback/src/main/res/values/strings.xml @@ -1531,11 +1531,11 @@ Buttons Voice for speaking images Images - Voice for speaking actionable elements on web pages - Actionable elements on web pages - Voice for speaking switches and checkboxes + Voice for speaking\nactionable web elements + Actionable web elements + Voice for speaking\nswitches and checkboxes Switches and checkboxes - Voice for speaking selected or checked items + Voice for speaking\nselected or checked items Selected or checked items From d6357dc31b1f78ca1bfc5b702a341461d6b709c9 Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Wed, 25 Mar 2020 20:29:45 +0300 Subject: [PATCH 5/7] Unchanged voice for disabled elements --- compositor/src/main/res/raw/compositor.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index 5f3b4274d..392888b90 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -1804,6 +1804,11 @@ ] }, "get_voice_for_element": { + "if": "$node.isEnabled", + "then": "%element_type_specific_voice", + "else": "%voice_normal" + }, + "element_type_specific_voice": { "if": "$node.isSelected || $node.isChecked || $node.isCheckedInternally", "then": "%selected_item_voice", "else": { From ca02f1c3c286f3d4c9e98eb356cb90206648fedf Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Wed, 25 Mar 2020 20:41:49 +0300 Subject: [PATCH 6/7] Clickable images are voicified as buttons --- compositor/src/main/res/raw/compositor.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index 392888b90..3c5524009 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -1823,7 +1823,11 @@ "toggle_button": "%switch_voice", "switch": "%switch_voice", "check_box": "%switch_voice", - "image": "%image_voice" + "image": { + "if": "$node.isClickable", + "then": "%button_voice", + "else": "%image_voice" + } }, "default": { "if": "$node.containsCheckable", From 6aa17bb9be6cfce0bd2c9edf383f69b69e0e7041 Mon Sep 17 00:00:00 2001 From: "Igor B. Poretsky" Date: Fri, 27 Mar 2020 02:29:53 +0300 Subject: [PATCH 7/7] Voice for headers --- compositor/src/main/java/Compositor.java | 12 ++++++++++++ compositor/src/main/res/raw/compositor.json | 16 +++++++++++++++- talkback/src/main/java/TalkBackService.java | 3 +++ talkback/src/main/res/values-ru/strings.xml | 2 ++ talkback/src/main/res/values/donottranslate.xml | 1 + talkback/src/main/res/values/strings.xml | 2 ++ .../src/main/res/xml/verbosity_preferences.xml | 10 ++++++++++ 7 files changed, 45 insertions(+), 1 deletion(-) diff --git a/compositor/src/main/java/Compositor.java b/compositor/src/main/java/Compositor.java index d451a51d3..001caded3 100644 --- a/compositor/src/main/java/Compositor.java +++ b/compositor/src/main/java/Compositor.java @@ -280,6 +280,7 @@ static class Constants { @VoiceType int mButtonVoice = VOICE_TYPE_NORMAL; @VoiceType int mSwitchVoice = VOICE_TYPE_NORMAL; @VoiceType int mSelectedItemVoice = VOICE_TYPE_NORMAL; + @VoiceType int mHeaderVoice = VOICE_TYPE_NORMAL; @VoiceType int mImageVoice = VOICE_TYPE_NORMAL; boolean mSpeakElementIds = false; } @@ -377,6 +378,13 @@ public void setSelectedItemVoice(@VoiceType int voiceType) { } } + public void setHeaderVoice(@VoiceType int voiceType) { + if (voiceType != mConstants.mHeaderVoice) { + mConstants.mHeaderVoice = voiceType; + mParseTreeIsStale = true; + } + } + public void setImageVoice(@VoiceType int voiceType) { if (voiceType != mConstants.mImageVoice) { mConstants.mImageVoice = voiceType; @@ -758,6 +766,10 @@ private static void declareConstants(ParseTree parseTree, Constants constants) { "SELECTED_ITEM_VOICE", ENUM_VOICE_TYPE, constants.mSelectedItemVoice); + parseTree.setConstantEnum( + "HEADER_VOICE", + ENUM_VOICE_TYPE, + constants.mHeaderVoice); parseTree.setConstantEnum( "IMAGE_VOICE", ENUM_VOICE_TYPE, diff --git a/compositor/src/main/res/raw/compositor.json b/compositor/src/main/res/raw/compositor.json index 3c5524009..fa221c9a9 100644 --- a/compositor/src/main/res/raw/compositor.json +++ b/compositor/src/main/res/raw/compositor.json @@ -1832,11 +1832,25 @@ "default": { "if": "$node.containsCheckable", "then": "%switch_voice", - "else": "%voice_normal" + "else": { + "if": "$node.isHeading", + "then": "%header_voice", + "else": "%voice_normal" + } } } } }, + "header_voice": { + "switch": "#HEADER_VOICE", + "cases": { + "low": "%voice_low", + "reduced": "%voice_reduced", + "normal": "%voice_normal", + "elevated": "%voice_elevated", + "high": "%voice_high" + } + }, "actionable_element_voice": { "switch": "#ACTIONABLE_ELEMENT_VOICE", "cases": { diff --git a/talkback/src/main/java/TalkBackService.java b/talkback/src/main/java/TalkBackService.java index d77e068ca..074c97031 100644 --- a/talkback/src/main/java/TalkBackService.java +++ b/talkback/src/main/java/TalkBackService.java @@ -1889,6 +1889,9 @@ private void reloadPreferences() { voiceType = SharedPreferencesUtils.getStringPref( prefs, res, R.string.pref_selected_item_voice_key, R.string.pref_voice_default); compositor.setSelectedItemVoice(prefValueToVoiceType(res, voiceType)); + voiceType = SharedPreferencesUtils.getStringPref( + prefs, res, R.string.pref_header_voice_key, R.string.pref_voice_default); + compositor.setHeaderVoice(prefValueToVoiceType(res, voiceType)); voiceType = SharedPreferencesUtils.getStringPref( prefs, res, R.string.pref_image_voice_key, R.string.pref_voice_default); compositor.setImageVoice(prefValueToVoiceType(res, voiceType)); diff --git a/talkback/src/main/res/values-ru/strings.xml b/talkback/src/main/res/values-ru/strings.xml index 169527af1..a7f87b7ee 100644 --- a/talkback/src/main/res/values-ru/strings.xml +++ b/talkback/src/main/res/values-ru/strings.xml @@ -543,5 +543,7 @@ Флажки и переключатели Голос для озвучивания\nвыбранных элементов Выбранные элементы + Голос для озвучивания заголовков + Заголовки diff --git a/talkback/src/main/res/values/donottranslate.xml b/talkback/src/main/res/values/donottranslate.xml index f7362b71a..1c3aa6f18 100644 --- a/talkback/src/main/res/values/donottranslate.xml +++ b/talkback/src/main/res/values/donottranslate.xml @@ -613,5 +613,6 @@ button_voice_pitch switch_voice_pitch selected_item_voice_pitch + header_voice_pitch diff --git a/talkback/src/main/res/values/strings.xml b/talkback/src/main/res/values/strings.xml index e522ae0f4..f09a46070 100644 --- a/talkback/src/main/res/values/strings.xml +++ b/talkback/src/main/res/values/strings.xml @@ -1537,5 +1537,7 @@ Switches and checkboxes Voice for speaking\nselected or checked items Selected or checked items + Voice for speaking headers + Headers diff --git a/talkback/src/main/res/xml/verbosity_preferences.xml b/talkback/src/main/res/xml/verbosity_preferences.xml index ab775d3a8..b57ad8684 100644 --- a/talkback/src/main/res/xml/verbosity_preferences.xml +++ b/talkback/src/main/res/xml/verbosity_preferences.xml @@ -127,6 +127,16 @@ android:summary="%s" android:title="@string/pref_actionable_voice_title" /> + + +