Logging added for ax mode and updated to fix console warnings

Summary: Added logging for accessibility functionality (both usage and performance). Fix to prevent trackTimeSince calls from not matching up to the correct marks. Fix to prevent recursive onElementExpanded calls from mismatching also.

Reviewed By: danielbuechele

Differential Revision: D9229790

fbshipit-source-id: d20f08719d2c4f9a35c9c71a492619ce5538d204
This commit is contained in:
Sara Valderrama
2018-08-10 14:52:56 -07:00
committed by Facebook Github Bot
parent e50bbd861d
commit bf863c3922
3 changed files with 95 additions and 22 deletions

View File

@@ -391,6 +391,11 @@ public class InspectorSonarPlugin implements SonarPlugin {
// if in layout inspector and talkback is running, override the first click to locate the clicked view
if (mConnection != null && AccessibilityUtil.isTalkbackEnabled(getContext()) && event.getPointerCount() == 1) {
SonarObject params = new SonarObject.Builder()
.put("type", "usage")
.put("eventName", "accessibility:clickToInspectTalkbackRunning").build();
mConnection.send("track", params);
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER: {

View File

@@ -75,7 +75,8 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
for (View view : node.getViewRoots()) {
// unlikely, but check to make sure accessibility functionality doesn't change
if (view instanceof ViewGroup && !ViewCompat.hasAccessibilityDelegate(view)) {
boolean hasDelegateAlready = ViewCompat.hasAccessibilityDelegate(view);
if (view instanceof ViewGroup && !hasDelegateAlready) {
// add delegate to root to catch accessibility events so we can update focus in sonar
view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@@ -107,6 +108,11 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
}
});
editedDelegates.add((ViewGroup) view);
} else if (hasDelegateAlready) {
SonarObject params = new SonarObject.Builder()
.put("type", "usage")
.put("eventName", "accessibility:hasDelegateAlready").build();
mConnection.send("track", params);
}
}
}

View File

@@ -25,6 +25,8 @@ import {
VerticalRule,
} from 'sonar';
import type {TrackType} from '../../fb-stubs/Logger.js';
import {
AXElementsInspector,
AXToggleButtonEnabled,
@@ -50,6 +52,7 @@ export type InspectorState = {|
inAXMode: boolean,
AXtoNonAXMapping: {[key: ElementID]: ElementID},
isAlignmentMode: boolean,
logCounter: number,
|};
type SelectElementArgs = {|
@@ -93,6 +96,12 @@ type GetNodesOptions = {|
forFocusEvent?: boolean,
|};
type TrackArgs = {|
type: TrackType,
eventName: string,
data?: any,
|};
type SearchResultTree = {|
id: string,
isMatch: Boolean,
@@ -184,6 +193,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
AXfocused: null,
AXtoNonAXMapping: {},
isAlignmentMode: false,
logCounter: 0,
};
reducers = {
@@ -480,15 +490,22 @@ export default class Layout extends SonarPlugin<InspectorState> {
}
initAX() {
performance.mark('InitAXRoot');
this.client.call('getAXRoot').then((element: Element) => {
this.dispatchAction({elements: [element], type: 'UpdateAXElements'});
this.dispatchAction({root: element.id, type: 'SetAXRoot'});
this.performInitialExpand(element, true).then(() => {
this.props.logger.trackTimeSince('InitAXRoot', 'accessibility:getRoot');
this.setState({AXinitialised: true});
});
});
this.client.subscribe('axFocusEvent', ({isFocus}: AXFocusEventResult) => {
this.props.logger.track('usage', 'accessibility:focusEvent', {
isFocus,
inAXMode: this.state.inAXMode,
});
// if focusing, need to update all elements in the tree because
// we don't know which one now has focus
const keys = isFocus ? Object.keys(this.state.AXelements) : [];
@@ -526,8 +543,15 @@ export default class Layout extends SonarPlugin<InspectorState> {
);
this.client.subscribe('selectAX', ({path}: {path: Array<ElementID>}) => {
if (this.state.inAXMode) {
this.props.logger.track('usage', 'accessibility:clickToInspect');
}
this.onSelectResultsRecieved(path, true);
});
this.client.subscribe('track', ({type, eventName, data}: TrackArgs) => {
this.props.logger.track(type, eventName, data);
});
}
init() {
@@ -563,6 +587,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
});
if (this.axEnabled()) {
this.props.logger.track('usage', 'accessibility:init');
this.initAX();
}
}
@@ -626,22 +651,32 @@ export default class Layout extends SonarPlugin<InspectorState> {
): Promise<Array<Element>> {
const {force, ax, forFocusEvent} = options;
if (!force) {
const elems = ax ? this.state.AXelements : this.state.elements;
// always force undefined elements and elements that need to be expanded
// over in the main tree (e.g. fragments)
ids = ids.filter(id => {
return (
(ax ? this.state.AXelements : this.state.elements)[id] === undefined
!elems[id] ||
(elems[id].extraInfo && elems[id].extraInfo.nonAXWithAXChild)
);
});
}
if (ids.length > 0) {
performance.mark('LayoutInspectorGetNodes');
// prevents overlapping calls from interfering with each other's logging
const mark = 'LayoutInspectorGetNodes' + this.state.logCounter++;
const eventName = ax
? 'accessibility:getNodes'
: 'LayoutInspectorGetNodes';
performance.mark(mark);
return this.client
.call(ax ? 'getAXNodes' : 'getNodes', {
ids,
forFocusEvent,
})
.then(({elements}: GetNodesResult) => {
this.props.logger.trackTimeSince('LayoutInspectorGetNodes');
this.props.logger.trackTimeSince(mark, eventName);
return Promise.resolve(elements);
});
} else {
@@ -670,13 +705,20 @@ export default class Layout extends SonarPlugin<InspectorState> {
key,
type: ax ? 'ExpandAXElement' : 'ExpandElement',
});
performance.mark('LayoutInspectorExpandElement');
const mark = ax ? 'ExpandAXElement' : 'LayoutInspectorExpandElement';
const eventName = ax
? 'accessibility:expandElement'
: 'LayoutInspectorExpandElement';
performance.mark(mark);
if (expand) {
return this.getChildren(key, ax).then((elements: Array<Element>) => {
this.dispatchAction({
elements,
type: ax ? 'UpdateAXElements' : 'UpdateElements',
});
this.props.logger.trackTimeSince(mark, eventName);
// only expand extra components in the main tree when in AX mode
if (this.state.inAXMode && !ax) {
@@ -687,8 +729,6 @@ export default class Layout extends SonarPlugin<InspectorState> {
}
}
}
this.props.logger.trackTimeSince('LayoutInspectorExpandElement');
return Promise.resolve(elements);
});
} else {
@@ -731,6 +771,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
} else {
this.expandElement(key, false);
}
this.props.logger.track('usage', 'layout:element-expanded', {
id: key,
deep: deep,
});
}
if (this.state.AXelements[key]) {
@@ -739,12 +783,13 @@ export default class Layout extends SonarPlugin<InspectorState> {
} else {
this.expandElement(key, true);
}
if (this.state.inAXMode) {
this.props.logger.track('usage', 'accessibility:elementExpanded', {
id: key,
deep: deep,
});
}
}
this.props.logger.track('usage', 'layout:element-expanded', {
id: key,
deep: deep,
});
};
onFindClick = () => {
@@ -755,8 +800,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
onToggleAccessibility = () => {
const inAXMode = !this.state.inAXMode;
this.props.logger.track('usage', 'accessibility:modeToggled', {inAXMode});
this.dispatchAction({inAXMode, type: 'SetAXMode'});
};
onToggleAlignment = () => {
const isAlignmentMode = !this.state.isAlignmentMode;
this.dispatchAction({isAlignmentMode, type: 'SetAlignmentActive'});
@@ -778,11 +825,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
// element only in AX tree with linked nonAX (litho) element selected
} else if (
(!this.state.elements[selectedKey] ||
this.state.elements[selectedKey].name === 'ComponentHost') &&
this.state.AXtoNonAXMapping[selectedKey]
!this.state.elements[selectedKey] ||
this.state.elements[selectedKey].name === 'ComponentHost'
) {
key = this.state.AXtoNonAXMapping[selectedKey];
key = this.state.AXtoNonAXMapping[selectedKey] || null;
AXkey = selectedKey;
// keys are same for both trees or 'linked' element does not exist
@@ -802,15 +848,20 @@ export default class Layout extends SonarPlugin<InspectorState> {
AXkey: AXkey,
type: 'SelectElement',
});
this.client.send('setHighlighted', {
id: selectedKey,
isAlignmentMode: this.state.isAlignmentMode,
});
this.getNodes([key], {force: true, ax: false}).then(
(elements: Array<Element>) => {
this.dispatchAction({elements, type: 'UpdateElements'});
},
);
if (key) {
this.getNodes([key], {force: true, ax: false}).then(
(elements: Array<Element>) => {
this.dispatchAction({elements, type: 'UpdateElements'});
},
);
}
if (AXkey) {
this.getNodes([AXkey], {force: true, ax: true}).then(
(elements: Array<Element>) => {
@@ -818,6 +869,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
},
);
}
if (this.state.inAXMode) {
this.props.logger.track('usage', 'accessibility:selectElement');
}
});
onElementHovered = debounce((key: ?ElementID) => {
@@ -852,7 +907,14 @@ export default class Layout extends SonarPlugin<InspectorState> {
}
});
this.props.logger.track('usage', 'layout:value-changed', {id, value, path});
const eventName = ax
? 'accessibility:dataValueChanged'
: 'layout:value-changed';
this.props.logger.track('usage', eventName, {
id,
value,
path,
});
};
renderSidebar = () => {