Improve initial display selection and naming.
Displays without a known name property should be labelled "Unknown Display".
The initial display selected should default to one with non-display rects, if possible.
Fixes: 327325638
Test: npm run test:presubmit:quiet
Change-Id: I135dd48ddb3d40e266f27d7692db4f3faab14f9c
diff --git a/tools/winscope/src/parsers/surface_flinger/computations/rects_computation.ts b/tools/winscope/src/parsers/surface_flinger/computations/rects_computation.ts
index 94b0e76..762cefc 100644
--- a/tools/winscope/src/parsers/surface_flinger/computations/rects_computation.ts
+++ b/tools/winscope/src/parsers/surface_flinger/computations/rects_computation.ts
@@ -30,7 +30,8 @@
const layerStack = assertDefined(
display.getChildByName('layerStack'),
).getValue();
- let displayName = display.getChildByName('name')?.getValue() ?? '';
+ let displayName =
+ display.getChildByName('name')?.getValue() ?? 'Unknown Display';
const id = assertDefined(display.getChildByName('id')).getValue();
const existingNameCount = nameCounts.get(displayName);
diff --git a/tools/winscope/src/parsers/surface_flinger/computations/rects_computation_test.ts b/tools/winscope/src/parsers/surface_flinger/computations/rects_computation_test.ts
index 54c23a8..6528aab 100644
--- a/tools/winscope/src/parsers/surface_flinger/computations/rects_computation_test.ts
+++ b/tools/winscope/src/parsers/surface_flinger/computations/rects_computation_test.ts
@@ -268,6 +268,44 @@
expect(hierarchyRoot.getRects()).toEqual(expectedDisplayRects);
});
+ it('makes display rects with unknown name', () => {
+ const hierarchyRoot = new HierarchyTreeBuilder()
+ .setId('LayerTraceEntry')
+ .setName('root')
+ .setProperties({
+ displays: [
+ {
+ id: 1,
+ layerStack: 0,
+ size: {w: 5, h: 5},
+ transform: Transform.EMPTY,
+ },
+ ],
+ })
+ .build();
+
+ const expectedDisplayRects = [
+ new TraceRectBuilder()
+ .setX(0)
+ .setY(0)
+ .setWidth(5)
+ .setHeight(5)
+ .setId('Display - 1')
+ .setName('Unknown Display')
+ .setCornerRadius(0)
+ .setTransform(Transform.EMPTY.matrix)
+ .setDepth(0)
+ .setGroupId(0)
+ .setIsVisible(false)
+ .setIsDisplay(true)
+ .setIsVirtual(false)
+ .build(),
+ ];
+
+ computation.setRoot(hierarchyRoot).executeInPlace();
+ expect(hierarchyRoot.getRects()).toEqual(expectedDisplayRects);
+ });
+
it('handles z-order paths with different lengths', () => {
const hierarchyRoot = new HierarchyTreeBuilder()
.setId('LayerTraceEntry')
diff --git a/tools/winscope/src/viewers/components/rects/rects_component.ts b/tools/winscope/src/viewers/components/rects/rects_component.ts
index 538cd3d..bb04d73 100644
--- a/tools/winscope/src/viewers/components/rects/rects_component.ts
+++ b/tools/winscope/src/viewers/components/rects/rects_component.ts
@@ -21,6 +21,7 @@
Input,
OnDestroy,
OnInit,
+ SimpleChange,
SimpleChanges,
} from '@angular/core';
import {assertDefined} from 'common/assert_utils';
@@ -290,7 +291,10 @@
this.updateControlsFromStore();
}
- this.currentDisplay = this.internalDisplays[0] ?? undefined;
+ this.currentDisplay =
+ this.internalDisplays.length > 0
+ ? this.getFirstDisplayWithRectsOrFirstDisplay(this.internalDisplays)
+ : undefined;
this.mapper3d.setCurrentGroupId(this.currentDisplay?.groupId ?? 0);
this.mapper3d.increaseZoomFactor(this.zoomFactor - 1);
this.drawLargeRectsAndLabels();
@@ -320,7 +324,7 @@
}
}
if (simpleChanges['displays']) {
- this.onDisplaysChange(simpleChanges['displays'].currentValue);
+ this.onDisplaysChange(simpleChanges['displays']);
if (this.isStackBased) {
this.internalGroupIds = new Set(
this.internalDisplays.map((display) => display.groupId),
@@ -337,13 +341,21 @@
this.resizeObserver?.disconnect();
}
- onDisplaysChange(displays: DisplayIdentifier[]) {
+ onDisplaysChange(change: SimpleChange) {
+ const displays = change.currentValue;
this.internalDisplays = displays;
if (displays.length === 0) {
return;
}
+ if (change.firstChange) {
+ this.updateCurrentDisplay(
+ this.getFirstDisplayWithRectsOrFirstDisplay(this.internalDisplays),
+ );
+ return;
+ }
+
if (!this.stackSelected) {
const curr = this.internalDisplays.find(
(display) => display.displayId === this.currentDisplay?.displayId,
@@ -354,11 +366,13 @@
}
}
- const firstDisplayWithCurrentGroupId = this.internalDisplays.find(
+ const displaysWithCurrentGroupId = this.internalDisplays.filter(
(display) => display.groupId === this.mapper3d.getCurrentGroupId(),
);
- if (!firstDisplayWithCurrentGroupId) {
- this.updateCurrentDisplay(this.internalDisplays[0]);
+ if (displaysWithCurrentGroupId.length === 0) {
+ this.updateCurrentDisplay(
+ this.getFirstDisplayWithRectsOrFirstDisplay(this.internalDisplays),
+ );
return;
}
@@ -366,7 +380,9 @@
(display) => display.displayId === this.currentDisplay?.displayId,
);
if (!displayWithCurrentDisplayId) {
- this.updateCurrentDisplay(firstDisplayWithCurrentGroupId);
+ this.updateCurrentDisplay(
+ this.getFirstDisplayWithRectsOrFirstDisplay(displaysWithCurrentGroupId),
+ );
return;
}
@@ -467,7 +483,9 @@
displaysWithGroupId.length > 0 &&
!displaysWithGroupId.includes(this.currentDisplay)
) {
- this.updateCurrentDisplay(displaysWithGroupId[0]);
+ this.updateCurrentDisplay(
+ this.getFirstDisplayWithRectsOrFirstDisplay(displaysWithGroupId),
+ );
}
}
@@ -543,6 +561,18 @@
return this.currentDisplay.groupId === groupId ? 'primary' : 'secondary';
}
+ private getFirstDisplayWithRectsOrFirstDisplay(
+ displays: DisplayIdentifier[],
+ ): DisplayIdentifier {
+ return (
+ displays.find((display) =>
+ this.internalRects.some(
+ (rect) => !rect.isDisplay && rect.groupId === display.groupId,
+ ),
+ ) ?? assertDefined(displays.at(0))
+ );
+ }
+
private updateCurrentDisplay(display: DisplayIdentifier) {
this.currentDisplay = display;
this.mapper3d.setCurrentGroupId(display.groupId);
diff --git a/tools/winscope/src/viewers/components/rects/rects_component_test.ts b/tools/winscope/src/viewers/components/rects/rects_component_test.ts
index fe4cecf..21f04d9 100644
--- a/tools/winscope/src/viewers/components/rects/rects_component_test.ts
+++ b/tools/winscope/src/viewers/components/rects/rects_component_test.ts
@@ -55,7 +55,6 @@
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
- fixture.detectChanges();
});
it('can be created', () => {
@@ -78,31 +77,10 @@
});
it('draws scene when input data changes', async () => {
+ fixture.detectChanges();
spyOn(Canvas.prototype, 'draw').and.callThrough();
- const inputRect = new UiRectBuilder()
- .setX(0)
- .setY(0)
- .setWidth(1)
- .setHeight(1)
- .setLabel('rectangle1')
- .setTransform({
- dsdx: 1,
- dsdy: 0,
- dtdx: 0,
- dtdy: 1,
- tx: 0,
- ty: 0,
- })
- .setIsVisible(true)
- .setIsDisplay(false)
- .setId('test-id-1234')
- .setGroupId(0)
- .setIsVirtual(false)
- .setIsClickable(false)
- .setCornerRadius(0)
- .setDepth(0)
- .build();
+ const inputRect = makeRectWithGroupId(0);
expect(Canvas.prototype.draw).toHaveBeenCalledTimes(0);
component.rects = [inputRect];
@@ -114,6 +92,7 @@
});
it('draws scene when rotation slider changes', () => {
+ fixture.detectChanges();
spyOn(Canvas.prototype, 'draw').and.callThrough();
const slider = assertDefined(htmlElement.querySelector('.slider-rotation'));
@@ -124,6 +103,7 @@
});
it('draws scene when spacing slider changes', () => {
+ fixture.detectChanges();
spyOn(Canvas.prototype, 'draw').and.callThrough();
const slider = assertDefined(htmlElement.querySelector('.slider-spacing'));
@@ -134,6 +114,7 @@
});
it('unfocuses spacing slider on click', () => {
+ fixture.detectChanges();
const spacingSlider = assertDefined(
htmlElement.querySelector('.slider-spacing'),
);
@@ -141,6 +122,7 @@
});
it('unfocuses rotation slider on click', () => {
+ fixture.detectChanges();
const rotationSlider = assertDefined(
htmlElement.querySelector('.slider-rotation'),
);
@@ -406,21 +388,56 @@
});
it('uses stored rects view settings', () => {
- expect(component.rectsComponent.getZSpacingFactor()).toEqual(1);
- component.rectsComponent.onSeparationSliderChange(0.06);
fixture.detectChanges();
- expect(component.rectsComponent.getZSpacingFactor()).toEqual(0.06);
+ const rectsComponent = assertDefined(component.rectsComponent);
+ expect(rectsComponent.getZSpacingFactor()).toEqual(1);
+ rectsComponent.onSeparationSliderChange(0.06);
+ fixture.detectChanges();
+ expect(rectsComponent.getZSpacingFactor()).toEqual(0.06);
- expect(component.rectsComponent.getShowOnlyVisibleMode()).toBeFalse();
+ expect(rectsComponent.getShowOnlyVisibleMode()).toBeFalse();
findAndClickCheckbox('.top-view-controls .show-only-visible input');
- expect(component.rectsComponent.getShowOnlyVisibleMode()).toBeTrue();
+ expect(rectsComponent.getShowOnlyVisibleMode()).toBeTrue();
const newFixture = TestBed.createComponent(TestHostComponent);
- const newComponent = newFixture.componentInstance;
newFixture.detectChanges();
+ const newRectsComponent = assertDefined(
+ newFixture.componentInstance.rectsComponent,
+ );
+ expect(newRectsComponent.getZSpacingFactor()).toEqual(0.06);
+ expect(newRectsComponent.getShowOnlyVisibleMode()).toBeTrue();
+ });
- expect(newComponent.rectsComponent.getZSpacingFactor()).toEqual(0.06);
- expect(newComponent.rectsComponent.getShowOnlyVisibleMode()).toBeTrue();
+ it('defaults initial selection to first display with non-display rects and groupId 0', () => {
+ const inputRect = makeRectWithGroupId(0);
+ component.rects = [inputRect];
+ component.displays = [
+ {displayId: 10, groupId: 1, name: 'Display 0'},
+ {displayId: 20, groupId: 0, name: 'Display 1'},
+ ];
+ fixture.detectChanges();
+
+ checkButtons(
+ ['Display 0', 'Display 1'],
+ ['secondary', 'primary'],
+ '.display-name-buttons',
+ );
+ });
+
+ it('defaults initial selection to first display with non-display rects and groupId non-zero', () => {
+ const inputRect = makeRectWithGroupId(1);
+ component.rects = [inputRect];
+ component.displays = [
+ {displayId: 10, groupId: 0, name: 'Display 0'},
+ {displayId: 20, groupId: 1, name: 'Display 1'},
+ ];
+ fixture.detectChanges();
+
+ checkButtons(
+ ['Display 0', 'Display 1'],
+ ['secondary', 'primary'],
+ '.display-name-buttons',
+ );
});
function checkButtons(
@@ -461,14 +478,41 @@
}
function checkSliderUnfocusesOnClick(slider: Element, expectedValue: number) {
+ const rectsComponent = assertDefined(component.rectsComponent);
slider.dispatchEvent(new MouseEvent('mousedown'));
- expect(component.rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
+ expect(rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
htmlElement.dispatchEvent(
new KeyboardEvent('keydown', {key: 'ArrowRight'}),
);
- expect(component.rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
+ expect(rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
htmlElement.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowLeft'}));
- expect(component.rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
+ expect(rectsComponent.getZSpacingFactor()).toEqual(expectedValue);
+ }
+
+ function makeRectWithGroupId(groupId: number): UiRect {
+ return new UiRectBuilder()
+ .setX(0)
+ .setY(0)
+ .setWidth(1)
+ .setHeight(1)
+ .setLabel('rectangle1')
+ .setTransform({
+ dsdx: 1,
+ dsdy: 0,
+ dtdx: 0,
+ dtdy: 1,
+ tx: 0,
+ ty: 0,
+ })
+ .setIsVisible(true)
+ .setIsDisplay(false)
+ .setId('test-id-1234')
+ .setGroupId(groupId)
+ .setIsVirtual(false)
+ .setIsClickable(false)
+ .setCornerRadius(0)
+ .setDepth(0)
+ .build();
}
@Component({
@@ -489,6 +533,6 @@
isStackBased = false;
@ViewChild(RectsComponent)
- rectsComponent!: RectsComponent;
+ rectsComponent: RectsComponent | undefined;
}
});