From c07d8c14a43d1c3c00533f28b3b8c99333e0cad4 Mon Sep 17 00:00:00 2001 From: Sara Valderrama Date: Tue, 7 Aug 2018 09:35:14 -0700 Subject: [PATCH] Add icon to show which elements in ax tree are talkback-focusable Summary: Puts an accessibility icon next to any elements in the ax tree that may be focused on when talkback is running to show that they are "accessibility-focusable". When any sidebar values are changed, the icon will show up/disappear accordingly. Reviewed By: danielbuechele Differential Revision: D9171781 fbshipit-source-id: f3b42624988aaef22040ac3325d745a12f0622db --- .../inspector/InspectorSonarPlugin.java | 3 +++ .../plugins/inspector/NodeDescriptor.java | 8 ++++++ .../descriptors/TextViewDescriptor.java | 6 +++++ .../inspector/descriptors/ViewDescriptor.java | 6 +++++ .../descriptors/ViewGroupDescriptor.java | 6 +++++ .../plugins/litho/LithoViewDescriptor.java | 6 +++++ src/plugins/layout/index.js | 23 +++++++++++------- .../components/elements-inspector/elements.js | 2 ++ static/icons/accessibility.png | Bin 0 -> 7414 bytes 9 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 static/icons/accessibility.png diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java index cca578099..9658eaf64 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java @@ -283,6 +283,7 @@ public class InspectorSonarPlugin implements SonarPlugin { public void onReceiveOnMainThread(final SonarObject params, SonarResponder responder) throws Exception { final String nodeId = params.getString("id"); + final boolean ax = params.getBoolean("ax"); final SonarArray keyPath = params.getArray("path"); final SonarDynamic value = params.getDynamic("value"); @@ -303,6 +304,7 @@ public class InspectorSonarPlugin implements SonarPlugin { } descriptor.setValue(obj, path, value); + responder.success(ax ? getAXNode(nodeId): null); } }; @@ -620,6 +622,7 @@ public class InspectorSonarPlugin implements SonarPlugin { .put("data", data) .put("children", children) .put("attributes", attributes) + .put("decoration", descriptor.getAXDecoration(obj)) .put("extraInfo", descriptor.getExtraInfo(obj)) .build(); } diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/NodeDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/NodeDescriptor.java index 9f97c53c7..6fdab336d 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/NodeDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/NodeDescriptor.java @@ -183,6 +183,14 @@ public abstract class NodeDescriptor { */ public abstract String getDecoration(T node) throws Exception; + /** + * @return A string indicating how this element should be decorated in the AX tree. Check with the Sonar desktop + * app to see what values are supported. + */ + public String getAXDecoration(T node) throws Exception { + return null; + } + /** * @return Extra data about the node indicating whether the node corresponds to a node in the * other tree or if it is not represented in the other tree bu has children that should show diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/TextViewDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/TextViewDescriptor.java index ee4fd3b97..70ce60ed9 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/TextViewDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/TextViewDescriptor.java @@ -155,6 +155,12 @@ public class TextViewDescriptor extends NodeDescriptor { return descriptor.getDecoration(node); } + @Override + public @Nullable String getAXDecoration(TextView node) throws Exception { + final NodeDescriptor descriptor = descriptorForClass(View.class); + return descriptor.getAXDecoration(node); + } + @Override public boolean matches(String query, TextView node) throws Exception { final NodeDescriptor descriptor = descriptorForClass(Object.class); diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java index 85078281f..67ce04b37 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java @@ -37,6 +37,7 @@ import com.facebook.sonar.plugins.inspector.InspectorValue; import com.facebook.sonar.plugins.inspector.Named; import com.facebook.sonar.plugins.inspector.NodeDescriptor; import com.facebook.sonar.plugins.inspector.Touch; +import com.facebook.sonar.plugins.inspector.descriptors.utils.AccessibilityEvaluationUtil; import com.facebook.sonar.plugins.inspector.descriptors.utils.AccessibilityRoleUtil; import com.facebook.sonar.plugins.inspector.descriptors.utils.AccessibilityUtil; import com.facebook.sonar.plugins.inspector.descriptors.utils.EnumMapping; @@ -538,6 +539,11 @@ public class ViewDescriptor extends NodeDescriptor { return null; } + @Override + public String getAXDecoration(View obj) { + return AccessibilityEvaluationUtil.isTalkbackFocusable(obj) ? "accessibility" : ""; + } + @Override public boolean matches(String query, View node) throws Exception { final String resourceId = getResourceId(node); diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewGroupDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewGroupDescriptor.java index 2a839b756..320a03996 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewGroupDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewGroupDescriptor.java @@ -291,6 +291,12 @@ public class ViewGroupDescriptor extends NodeDescriptor { return null; } + @Override + public @Nullable String getAXDecoration(ViewGroup obj) throws Exception { + final NodeDescriptor descriptor = descriptorForClass(View.class); + return descriptor.getAXDecoration(obj); + } + @Override public boolean matches(String query, ViewGroup node) throws Exception { final NodeDescriptor descriptor = descriptorForClass(Object.class); diff --git a/android/src/main/java/com/facebook/sonar/plugins/litho/LithoViewDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/litho/LithoViewDescriptor.java index 98d592220..c19f9f23e 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/litho/LithoViewDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/litho/LithoViewDescriptor.java @@ -141,6 +141,12 @@ public class LithoViewDescriptor extends NodeDescriptor { return descriptor.getDecoration(node); } + @Override + public String getAXDecoration(LithoView node) throws Exception { + final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class); + return descriptor.getAXDecoration(node); + } + @Override public boolean matches(String query, LithoView node) throws Exception { NodeDescriptor descriptor = descriptorForClass(Object.class); diff --git a/src/plugins/layout/index.js b/src/plugins/layout/index.js index 4df7d70d6..4bb1dd3a1 100644 --- a/src/plugins/layout/index.js +++ b/src/plugins/layout/index.js @@ -803,15 +803,20 @@ export default class Layout extends SonarPlugin { } onDataValueChanged = (path: Array, value: any) => { - const selected = this.state.inAXMode - ? this.state.AXselected - : this.state.selected; - this.client.send('setData', {id: selected, path, value}); - this.props.logger.track('usage', 'layout:value-changed', { - id: selected, - value: value, - path: path, - }); + const ax = this.state.inAXMode; + const id = ax ? this.state.AXselected : this.state.selected; + this.client + .call('setData', {id, path, value, ax}) + .then((element: Element) => { + if (ax) { + this.dispatchAction({ + elements: [element], + type: 'UpdateAXElements', + }); + } + }); + + this.props.logger.track('usage', 'layout:value-changed', {id, value, path}); }; renderSidebar = () => { diff --git a/src/ui/components/elements-inspector/elements.js b/src/ui/components/elements-inspector/elements.js index c4c56907c..6e21b7253 100644 --- a/src/ui/components/elements-inspector/elements.js +++ b/src/ui/components/elements-inspector/elements.js @@ -331,6 +331,8 @@ class ElementsRow extends PureComponent { return ; case 'componentscript': return ; + case 'accessibility': + return ; default: return null; } diff --git a/static/icons/accessibility.png b/static/icons/accessibility.png new file mode 100644 index 0000000000000000000000000000000000000000..65673375024f027b57cdd7dcbf070dae47a6c838 GIT binary patch literal 7414 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