[DO NOT MERGE] Full - Compatibilize winscope with master
Bug: 194813677 Test: none Change-Id: Ie55429dcb746f597f80dfdaa68cbef19f5820005
This commit is contained in:
@@ -115,6 +115,11 @@ TRACE_TARGETS = {
|
||||
'su root cmd window tracing start\necho "WM trace started."',
|
||||
'su root cmd window tracing stop >/dev/null 2>&1'
|
||||
),
|
||||
"accessibility_trace": TraceTarget(
|
||||
File("/data/misc/a11ytrace/a11y_trace.pb", "accessibility_trace"),
|
||||
'su root cmd accessibility start-trace\necho "Accessibility trace started."',
|
||||
'su root cmd accessibility stop-trace >/dev/null 2>&1'
|
||||
),
|
||||
"layers_trace": TraceTarget(
|
||||
File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"),
|
||||
'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."',
|
||||
|
||||
@@ -5,60 +5,60 @@
|
||||
"author": "Adrian Roos <roosa@google.com>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
|
||||
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
|
||||
"dev": "cross-env NODE_ENV=development webpack serve --open --hot",
|
||||
"build": "cross-env NODE_ENV=production webpack --progress",
|
||||
"test": "webpack --config webpack.spec.config.js && jasmine dist/bundleSpec.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.2",
|
||||
"jszip": "^3.5.0",
|
||||
"kotlin": "^1.3.72",
|
||||
"cross-env": "^7.0.3",
|
||||
"jszip": "^3.6.0",
|
||||
"kotlin": "^1.5.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"ts-loader": "^8.0.3",
|
||||
"typescript": "^4.0.2",
|
||||
"vue": "^2.3.3",
|
||||
"vue-context": "^5.2.0",
|
||||
"vue-material": "^1.0.0-beta-11",
|
||||
"vuex": "^3.4.0"
|
||||
"ts-loader": "^8.3.0",
|
||||
"typescript": "^4.3.5",
|
||||
"vue": "^2.6.14",
|
||||
"vue-context": "^6.0.0",
|
||||
"vue-material": "^1.0.0-beta-15",
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/polyfill": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/register": "^7.10.5",
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/register": "^7.14.5",
|
||||
"@jetbrains/kotlin-webpack-plugin": "^3.0.2",
|
||||
"@testing-library/vue": "^5.1.0",
|
||||
"@types/lodash": "^4.14.158",
|
||||
"babel-loader": "^8.1.0",
|
||||
"compression-webpack-plugin": "^4.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"eslint": "^7.1.0",
|
||||
"@testing-library/vue": "^5.8.1",
|
||||
"@types/lodash": "^4.14.171",
|
||||
"babel-loader": "^8.2.2",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^5.2.7",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"file-loader": "^6.0.0",
|
||||
"eslint-plugin-vue": "^7.13.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"html-webpack-inline-source-plugin": "^0.0.10",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "^4.2.5",
|
||||
"jasmine": "^3.5.0",
|
||||
"lint-staged": ">=10",
|
||||
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
|
||||
"html-webpack-plugin": "4.5.2",
|
||||
"husky": "^7.0.0",
|
||||
"jasmine": "^3.8.0",
|
||||
"lint-staged": "^11.0.1",
|
||||
"loader-utils": "^2.0.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"protobufjs": "^6.10.0",
|
||||
"source-map-loader": "^1.0.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^8.0.1",
|
||||
"typescript": "^3.9.7",
|
||||
"protobufjs": "^6.11.2",
|
||||
"source-map-loader": "^1.1.3",
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-loader": "^8.3.0",
|
||||
"typescript": "^4.3.5",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"vue-loader": "^15.9.3",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-merge": "^5.0.9"
|
||||
"vue-loader": "^15.9.2",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
35
tools/winscope/src/AccessibilityTraceView.vue
Normal file
35
tools/winscope/src/AccessibilityTraceView.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TraceView from "@/TraceView.vue"
|
||||
|
||||
export default {
|
||||
name: "AccessibilityTraceView",
|
||||
props: ["store", "file"],
|
||||
components: {
|
||||
TraceView
|
||||
},
|
||||
methods: {
|
||||
summarizer(item) {
|
||||
return null;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -36,7 +36,7 @@
|
||||
<dataadb class="adbinput" ref="adb" :store="store"
|
||||
@dataReady="onDataReady" @statusChange="setStatus" />
|
||||
</div>
|
||||
<div class="input">
|
||||
<div class="input" @dragover.prevent @drop.prevent>
|
||||
<datainput class="fileinput" ref="input" :store="store"
|
||||
@dataReady="onDataReady" @statusChange="setStatus" />
|
||||
</div>
|
||||
@@ -279,4 +279,18 @@ a {
|
||||
.data-view-container {
|
||||
padding: 25px 20px 0 20px;
|
||||
}
|
||||
|
||||
.snackbar-break-words {
|
||||
/* These are technically the same, but use both */
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
-ms-word-break: break-all;
|
||||
word-break: break-word;
|
||||
/* Adds a hyphen where the word breaks, if supported (No Blink) */
|
||||
-ms-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
padding: 10px 10px 10px 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -200,6 +200,7 @@ const DUMPS = {
|
||||
|
||||
const proxyFileTypeAdapter = {
|
||||
'window_trace': FILE_TYPES.WINDOW_MANAGER_TRACE,
|
||||
'accessibility_trace': FILE_TYPES.ACCESSIBILITY_TRACE,
|
||||
'layers_trace': FILE_TYPES.SURFACE_FLINGER_TRACE,
|
||||
'wl_trace': FILE_TYPES.WAYLAND_TRACE,
|
||||
'layers_dump': FILE_TYPES.SURFACE_FLINGER_DUMP,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<template>
|
||||
<div @dragleave="fileDragOut" @dragover="fileDragIn" @drop="handleFileDrop">
|
||||
<flat-card style="min-width: 50em">
|
||||
<md-card-header>
|
||||
<div class="md-title">Open files</div>
|
||||
@@ -37,16 +38,6 @@
|
||||
md-mode="indeterminate"
|
||||
v-show="loadingFiles"
|
||||
/>
|
||||
<div>
|
||||
<md-checkbox v-model="store.displayDefaults" class="md-primary">
|
||||
Show default properties
|
||||
<md-tooltip md-direction="bottom">
|
||||
If checked, shows the value of all properties.
|
||||
Otherwise, hides all properties whose value is the default for its
|
||||
data type.
|
||||
</md-tooltip>
|
||||
</md-checkbox>
|
||||
</div>
|
||||
<div class="md-layout">
|
||||
<div class="md-layout-item md-small-size-100">
|
||||
<md-field>
|
||||
@@ -100,7 +91,7 @@
|
||||
:md-active.sync="showSnackbar"
|
||||
md-persistent
|
||||
>
|
||||
<span style="white-space: pre-line;">{{ snackbarText }}</span>
|
||||
<p class="snackbar-break-words">{{ snackbarText }}</p>
|
||||
<div @click="hideSnackbarMessage()">
|
||||
<md-button class="md-icon-button">
|
||||
<md-icon style="color: white">close</md-icon>
|
||||
@@ -108,6 +99,7 @@
|
||||
</div>
|
||||
</md-snackbar>
|
||||
</flat-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FlatCard from './components/FlatCard.vue';
|
||||
@@ -145,7 +137,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
showSnackbarMessage(message, duration) {
|
||||
this.snackbarText = message;
|
||||
this.snackbarText = '\n' + message + '\n';
|
||||
this.snackbarDuration = duration;
|
||||
this.showSnackbar = true;
|
||||
},
|
||||
@@ -234,6 +226,18 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
fileDragIn(e){
|
||||
e.preventDefault();
|
||||
},
|
||||
fileDragOut(e){
|
||||
e.preventDefault();
|
||||
},
|
||||
handleFileDrop(e) {
|
||||
e.preventDefault();
|
||||
let droppedFiles = e.dataTransfer.files;
|
||||
if(!droppedFiles) return;
|
||||
this.processFiles(droppedFiles);
|
||||
},
|
||||
onLoadFile(e) {
|
||||
const files = event.target.files || event.dataTransfer.files;
|
||||
this.processFiles(files);
|
||||
|
||||
@@ -30,7 +30,12 @@
|
||||
<md-icon>save_alt</md-icon>
|
||||
</md-button>
|
||||
</md-card-header>
|
||||
|
||||
<AccessibilityTraceView
|
||||
v-if="showInAccessibilityTraceView(file)"
|
||||
:store="store"
|
||||
:file="file"
|
||||
ref="view"
|
||||
/>
|
||||
<WindowManagerTraceView
|
||||
v-if="showInWindowManagerTraceView(file)"
|
||||
:store="store"
|
||||
@@ -68,6 +73,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import TraceView from '@/TraceView.vue';
|
||||
import AccessibilityTraceView from '@/AccessibilityTraceView.vue';
|
||||
import WindowManagerTraceView from '@/WindowManagerTraceView.vue';
|
||||
import SurfaceFlingerTraceView from '@/SurfaceFlingerTraceView.vue';
|
||||
import TransactionsView from '@/TransactionsView.vue';
|
||||
@@ -152,6 +158,7 @@ export default {
|
||||
'transactionsview': TransactionsView,
|
||||
'logview': LogView,
|
||||
'flat-card': FlatCard,
|
||||
AccessibilityTraceView,
|
||||
WindowManagerTraceView,
|
||||
SurfaceFlingerTraceView,
|
||||
},
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
<template>
|
||||
<md-card-content class="container">
|
||||
<div class="navigation">
|
||||
<md-content
|
||||
md-tag="md-toolbar"
|
||||
md-elevation="0"
|
||||
class="card-toolbar md-transparent md-dense"
|
||||
>
|
||||
<h2 class="md-title" style="flex: 1">Log View</h2>
|
||||
<md-button
|
||||
class="md-dense md-primary"
|
||||
@click.native="scrollToRow(lastOccuredVisibleIndex)"
|
||||
@@ -33,6 +39,7 @@
|
||||
Pin to latest message
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
</md-content>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<vue-context ref="menu">
|
||||
<li>
|
||||
<a href="#" @click.prevent="$emit('collapseAllOtherNodes')">
|
||||
Collapse all other nodes
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</vue-context>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -67,6 +67,15 @@
|
||||
</div>
|
||||
|
||||
<div class="active-timeline" v-show="minimized">
|
||||
<md-field class="seek-timestamp-field">
|
||||
<label>Search for timestamp</label>
|
||||
<md-input v-model="searchTimestamp"></md-input>
|
||||
</md-field>
|
||||
|
||||
<md-button
|
||||
@click="updateSearchForTimestamp"
|
||||
>Search</md-button>
|
||||
|
||||
<div
|
||||
class="active-timeline-icon"
|
||||
@click="$refs.navigationTypeSelection.$el
|
||||
@@ -81,8 +90,9 @@
|
||||
</div>
|
||||
|
||||
<md-field
|
||||
v-if="multipleTraces"
|
||||
ref="navigationTypeSelection"
|
||||
class="nagivation-style-selection-field"
|
||||
class="navigation-style-selection-field"
|
||||
>
|
||||
|
||||
<label>Navigation</label>
|
||||
@@ -91,7 +101,8 @@
|
||||
name="navigationStyle"
|
||||
md-dense
|
||||
>
|
||||
<md-icon-option :value="NAVIGATION_STYLE.GLOBAL"
|
||||
<md-icon-option
|
||||
:value="NAVIGATION_STYLE.GLOBAL"
|
||||
icon="public"
|
||||
desc="Consider all timelines for navigation"
|
||||
/>
|
||||
@@ -265,7 +276,7 @@ import {NAVIGATION_STYLE} from './utils/consts';
|
||||
import {TRACE_ICONS} from '@/decode.js';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import {nanos_to_string} from './transform.js';
|
||||
import {nanos_to_string, string_to_nanos} from './transform.js';
|
||||
|
||||
export default {
|
||||
name: 'overlay',
|
||||
@@ -290,6 +301,7 @@ export default {
|
||||
crop: null,
|
||||
cropIntent: null,
|
||||
TRACE_ICONS,
|
||||
searchTimestamp: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -376,7 +388,7 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected nagivation type');
|
||||
throw new Error('Unexpected navigation type');
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -398,7 +410,7 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected nagivation type');
|
||||
throw new Error('Unexpected navigation type');
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -412,8 +424,12 @@ export default {
|
||||
}
|
||||
|
||||
if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) {
|
||||
//dumps do not have a timeline, so if scrolling over a dump, show merged timeline
|
||||
if (this.focusedFile.timeline) {
|
||||
return this.focusedFile;
|
||||
}
|
||||
return this.mergedTimeline;
|
||||
}
|
||||
|
||||
if (this.navigationStyle === NAVIGATION_STYLE.CUSTOM) {
|
||||
// TODO: Return custom timeline
|
||||
@@ -425,12 +441,15 @@ export default {
|
||||
.traces[this.navigationStyle.split('-')[1]];
|
||||
}
|
||||
|
||||
throw new Error('Unexpected Nagivation Style');
|
||||
throw new Error('Unexpected Navigation Style');
|
||||
},
|
||||
isCropped() {
|
||||
return this.crop != null &&
|
||||
(this.crop.left !== 0 || this.crop.right !== 1);
|
||||
},
|
||||
multipleTraces() {
|
||||
return this.timelineFiles.length > 1;
|
||||
},
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
@@ -589,7 +608,7 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected nagivation type');
|
||||
throw new Error('Unexpected navigation type');
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -623,6 +642,17 @@ export default {
|
||||
clearSelection() {
|
||||
this.crop = null;
|
||||
},
|
||||
updateSearchForTimestamp() {
|
||||
if (/^\d+$/.test(this.searchTimestamp)) {
|
||||
var roundedTimestamp = parseInt(this.searchTimestamp);
|
||||
} else {
|
||||
var roundedTimestamp = string_to_nanos(this.searchTimestamp);
|
||||
}
|
||||
var closestTimestamp = this.mergedTimeline.timeline.reduce(function(prev, curr) {
|
||||
return (Math.abs(curr-roundedTimestamp) < Math.abs(prev-roundedTimestamp) ? curr : prev);
|
||||
});
|
||||
this.$store.dispatch('updateTimelineTime', parseInt(closestTimestamp));
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'timeline': Timeline,
|
||||
@@ -816,7 +846,7 @@ export default {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.nagivation-style-selection-field {
|
||||
.navigation-style-selection-field {
|
||||
width: 90px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<script>
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import {multiply_rect} from './matrix_utils.js';
|
||||
import {multiplyRect} from './matrix_utils.js';
|
||||
|
||||
export default {
|
||||
name: 'rects',
|
||||
@@ -46,9 +46,9 @@ export default {
|
||||
return this.bounds;
|
||||
}
|
||||
const width = Math.max(
|
||||
...this.rects.map((r) => multiply_rect(r.transform, r).right));
|
||||
...this.rects.map((r) => multiplyRect(r.transform, r).right));
|
||||
const height = Math.max(
|
||||
...this.rects.map((r) => multiply_rect(r.transform, r).bottom));
|
||||
...this.rects.map((r) => multiplyRect(r.transform, r).bottom));
|
||||
return {width, height};
|
||||
},
|
||||
boundsStyle() {
|
||||
@@ -57,8 +57,9 @@ export default {
|
||||
},
|
||||
filteredRects() {
|
||||
return this.rects.filter((rect) => {
|
||||
const isVisible = rect.ref.visible ?? rect.ref.isVisible;
|
||||
console.warn(`Name: ${rect.ref.name} Kind: ${rect.ref.kind} isVisible=${isVisible}`);
|
||||
const isVisible = rect.ref.isVisible;
|
||||
console.warn(`Name: ${rect.ref.name}`, `Kind: ${rect.ref.kind}`,
|
||||
`isVisible=${isVisible}`);
|
||||
return isVisible;
|
||||
});
|
||||
},
|
||||
@@ -78,11 +79,23 @@ export default {
|
||||
const y = this.s(r.top);
|
||||
const w = this.s(r.right) - this.s(r.left);
|
||||
const h = this.s(r.bottom) - this.s(r.top);
|
||||
const t = r.transform;
|
||||
|
||||
let t;
|
||||
if (r.transform && r.transform.matrix) {
|
||||
t = r.transform.matrix;
|
||||
} else {
|
||||
t = r.transform;
|
||||
}
|
||||
|
||||
const tr = t ? `matrix(${t.dsdx}, ${t.dtdx}, ${t.dsdy}, ${t.dtdy}, ` +
|
||||
`${this.s(t.tx)}, ${this.s(t.ty)})` : '';
|
||||
return `top: ${y}px; left: ${x}px; height: ${h}px; width: ${w}px;` +
|
||||
const rectStyle = `top: ${y}px; left: ` +
|
||||
`${x}px; height: ${h}px; width: ${w}px; ` +
|
||||
`transform: ${tr}; transform-origin: 0 0;`;
|
||||
if (r && r.ref) {
|
||||
console.log(`${r.ref.name} - ${rectStyle}`);
|
||||
}
|
||||
return rectStyle;
|
||||
},
|
||||
onClick(r) {
|
||||
this.$emit('rect-click', r.ref);
|
||||
|
||||
@@ -30,23 +30,23 @@ export default {
|
||||
summarizer(layer) {
|
||||
const summary = [];
|
||||
|
||||
if (layer.invisibleDueTo) {
|
||||
summary.push({key: 'Invisible due to', value: layer.invisibleDueTo});
|
||||
if (layer?.visibilityReason) {
|
||||
summary.push({key: 'Invisible due to', value: layer.visibilityReason});
|
||||
}
|
||||
|
||||
if (layer.occludedBy?.length > 0) {
|
||||
summary.push({key: 'Occluded by', value: layer.occludedBy.join(', ')});
|
||||
if (layer?.occludedBy?.length > 0) {
|
||||
summary.push({key: 'Occluded by', value: layer.occludedBy.map(it => it.id).join(', ')});
|
||||
}
|
||||
|
||||
if (layer.partiallyOccludedBy?.length > 0) {
|
||||
if (layer?.partiallyOccludedBy?.length > 0) {
|
||||
summary.push({
|
||||
key: 'Partially occluded by',
|
||||
value: layer.partiallyOccludedBy.join(', '),
|
||||
value: layer.partiallyOccludedBy.map(it => it.id).join(', '),
|
||||
});
|
||||
}
|
||||
|
||||
if (layer.coveredBy?.length > 0) {
|
||||
summary.push({key: 'Covered by', value: layer.coveredBy.join(', ')});
|
||||
if (layer?.coveredBy?.length > 0) {
|
||||
summary.push({key: 'Covered by', value: layer.coveredBy.map(it => it.id).join(', ')});
|
||||
}
|
||||
|
||||
return summary;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
>
|
||||
<h2 class="md-title" style="flex: 1;">Hierarchy</h2>
|
||||
<md-checkbox
|
||||
v-model="showHierachyDiff"
|
||||
v-model="showHierarchyDiff"
|
||||
v-if="diffVisualizationAvailable"
|
||||
>
|
||||
Show Diff
|
||||
@@ -72,6 +72,19 @@
|
||||
class="card-toolbar md-transparent md-dense"
|
||||
>
|
||||
<h2 class="md-title" style="flex: 1">Properties</h2>
|
||||
<div>
|
||||
<md-checkbox
|
||||
v-model="displayDefaults"
|
||||
@change="checkboxChange"
|
||||
>
|
||||
Show Defaults
|
||||
</md-checkbox>
|
||||
<md-tooltip md-direction="bottom">
|
||||
If checked, shows the value of all properties.
|
||||
Otherwise, hides all properties whose value is
|
||||
the default for its data type.
|
||||
</md-tooltip>
|
||||
</div>
|
||||
<md-checkbox
|
||||
v-model="showPropertiesDiff"
|
||||
v-if="diffVisualizationAvailable"
|
||||
@@ -96,7 +109,6 @@
|
||||
:item="selectedTree"
|
||||
:filter="propertyFilter"
|
||||
:collapseChildren="true"
|
||||
:useGlobalCollapsedState="true"
|
||||
:elementView="PropertiesTreeElement"
|
||||
/>
|
||||
</div>
|
||||
@@ -104,7 +116,7 @@
|
||||
<i class="material-icons none-icon">
|
||||
filter_none
|
||||
</i>
|
||||
<span>No element selected in the hierachy.</span>
|
||||
<span>No element selected in the hierarchy.</span>
|
||||
</div>
|
||||
</div>
|
||||
</flat-card>
|
||||
@@ -123,6 +135,8 @@ import {DiffGenerator, defaultModifiedCheck} from './utils/diff.js';
|
||||
import {TRACE_TYPES, DUMP_TYPES} from './decode.js';
|
||||
import {stableIdCompatibilityFixup} from './utils/utils.js';
|
||||
import {CompatibleFeatures} from './utils/compatibility.js';
|
||||
import {getPropertiesForDisplay} from './flickerlib/mixin';
|
||||
import ObjectFormatter from './flickerlib/ObjectFormatter';
|
||||
|
||||
function formatProto(obj) {
|
||||
if (obj?.prettyPrint) {
|
||||
@@ -164,12 +178,16 @@ export default {
|
||||
item: null,
|
||||
tree: null,
|
||||
highlight: null,
|
||||
showHierachyDiff: false,
|
||||
showHierarchyDiff: false,
|
||||
displayDefaults: false,
|
||||
showPropertiesDiff: false,
|
||||
PropertiesTreeElement,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
checkboxChange(checked) {
|
||||
this.itemSelected(this.item);
|
||||
},
|
||||
itemSelected(item) {
|
||||
this.hierarchySelected = item;
|
||||
this.selectedTree = this.getTransformedProperties(item);
|
||||
@@ -178,8 +196,9 @@ export default {
|
||||
this.$emit('focus');
|
||||
},
|
||||
getTransformedProperties(item) {
|
||||
ObjectFormatter.displayDefaults = this.displayDefaults;
|
||||
const transformer = new ObjectTransformer(
|
||||
item.obj,
|
||||
getPropertiesForDisplay(item),
|
||||
item.name,
|
||||
stableIdCompatibilityFixup(item),
|
||||
).setOptions({
|
||||
@@ -189,7 +208,7 @@ export default {
|
||||
|
||||
if (this.showPropertiesDiff && this.diffVisualizationAvailable) {
|
||||
const prevItem = this.getItemFromPrevTree(item);
|
||||
transformer.withDiff(prevItem?.obj);
|
||||
transformer.withDiff(getPropertiesForDisplay(prevItem));
|
||||
}
|
||||
|
||||
return transformer.transform();
|
||||
@@ -200,12 +219,14 @@ export default {
|
||||
}
|
||||
},
|
||||
generateTreeFromItem(item) {
|
||||
if (!this.showHierachyDiff || !this.diffVisualizationAvailable) {
|
||||
if (!this.showHierarchyDiff || !this.diffVisualizationAvailable) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return new DiffGenerator(this.item)
|
||||
.compareWith(this.getDataWithOffset(-1))
|
||||
const thisItem = this.item;
|
||||
const prevItem = this.getDataWithOffset(-1);
|
||||
return new DiffGenerator(thisItem)
|
||||
.compareWith(prevItem)
|
||||
.withUniqueNodeId((node) => {
|
||||
return node.stableId;
|
||||
})
|
||||
@@ -216,7 +237,7 @@ export default {
|
||||
this.item = item;
|
||||
this.tree = this.generateTreeFromItem(item);
|
||||
|
||||
const rects = item.rects //.toArray()
|
||||
const rects = item.rects; // .toArray()
|
||||
this.rects = [...rects].reverse();
|
||||
this.bounds = item.bounds;
|
||||
|
||||
@@ -293,7 +314,7 @@ export default {
|
||||
selectedIndex() {
|
||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||
},
|
||||
showHierachyDiff() {
|
||||
showHierarchyDiff() {
|
||||
this.tree = this.generateTreeFromItem(this.item);
|
||||
},
|
||||
showPropertiesDiff() {
|
||||
@@ -317,7 +338,7 @@ export default {
|
||||
const hierarchyPropertyFilter =
|
||||
getFilter(this.hierarchyPropertyFilterString);
|
||||
return this.store.onlyVisible ? (c) => {
|
||||
return c.visible && hierarchyPropertyFilter(c);
|
||||
return c.isVisible && hierarchyPropertyFilter(c);
|
||||
} : hierarchyPropertyFilter;
|
||||
},
|
||||
propertyFilter() {
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
v-for="(surface, index) in sufacesAffectedBy(source)"
|
||||
v-bind:key="surface.id"
|
||||
>
|
||||
<span
|
||||
v-if="simplifyNames && surface.shortName &&
|
||||
surface.shortName !== surface.name"
|
||||
>{{surface.shortName}}>
|
||||
</span>
|
||||
<span v-else>
|
||||
<!-- eslint-disable-next-line max-len -->
|
||||
<span v-if="surface.name" class="surface-name">{{ surface.name }}</span>
|
||||
<span class="surface-id">
|
||||
@@ -42,6 +48,7 @@
|
||||
<!-- eslint-disable-next-line max-len -->
|
||||
<span v-if="index + 1 < sufacesAffectedBy(source).length">, </span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="extra-info-column">
|
||||
<span v-if="source.identifier">
|
||||
@@ -59,6 +66,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shortenName } from './flickerlib/mixin'
|
||||
|
||||
export default {
|
||||
name: 'transaction-entry',
|
||||
props: {
|
||||
@@ -83,6 +92,9 @@ export default {
|
||||
prettifyTransactionId: {
|
||||
type: Function,
|
||||
},
|
||||
simplifyNames: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentTimestamp() {
|
||||
@@ -135,8 +147,12 @@ export default {
|
||||
},
|
||||
sufacesAffectedBy(transaction) {
|
||||
if (transaction.type !== 'transaction') {
|
||||
// TODO (b/162402459): Shorten layer name
|
||||
return [{name: transaction.layerName, id: transaction.obj.id}];
|
||||
return [
|
||||
{
|
||||
name: transaction.layerName,
|
||||
shortName: shortenName(transaction.layerName),
|
||||
id: transaction.obj.id
|
||||
}];
|
||||
}
|
||||
|
||||
const surfaceIds = new Set();
|
||||
@@ -145,7 +161,12 @@ export default {
|
||||
const id = transaction.obj.id;
|
||||
if (!surfaceIds.has(id)) {
|
||||
surfaceIds.add(id);
|
||||
affectedSurfaces.push({name: transaction.layerName, id});
|
||||
affectedSurfaces.push(
|
||||
{
|
||||
name: transaction.layerName,
|
||||
shortName: shortenName(transaction.layerName),
|
||||
id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
<md-card-content class="container">
|
||||
|
||||
<flat-card class="changes card">
|
||||
<md-content
|
||||
md-tag="md-toolbar"
|
||||
md-elevation="0"
|
||||
class="card-toolbar md-transparent md-dense"
|
||||
>
|
||||
<h2 class="md-title" style="flex: 1">Transactions</h2>
|
||||
</md-content>
|
||||
<div class="filters">
|
||||
<div class="input">
|
||||
<md-field>
|
||||
@@ -75,6 +82,11 @@
|
||||
<div class="md-helper-text">Press enter to add</div>
|
||||
</md-chips>
|
||||
</div>
|
||||
|
||||
<md-checkbox v-model="trace.simplifyNames">
|
||||
Simplify names
|
||||
</md-checkbox>
|
||||
|
||||
</div>
|
||||
|
||||
<virtual-list style="height: 600px; overflow-y: auto;"
|
||||
@@ -86,6 +98,7 @@
|
||||
selectedTransaction,
|
||||
transactionsTrace,
|
||||
prettifyTransactionId,
|
||||
simplifyNames: trace.simplifyNames,
|
||||
}"
|
||||
ref="loglist"
|
||||
/>
|
||||
@@ -289,7 +302,6 @@ export default {
|
||||
const perpareForTreeViewTransform = (change) => {
|
||||
this.removeNullFields(change);
|
||||
change[META_DATA_KEY] = {
|
||||
// TODO (b/162402459): Shorten layer name
|
||||
layerName: change.layerName,
|
||||
};
|
||||
// remove redundant properties
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
v-on:selected="immediateChildSelected = true"
|
||||
v-on:unselected="immediateChildSelected = false"
|
||||
:elementView="elementView"
|
||||
v-on:collapseSibbling="collapseSibbling"
|
||||
v-on:collapseSibling="collapseSibling"
|
||||
v-on:collapseAllOtherNodes="collapseAllOtherNodes"
|
||||
v-on:closeAllContextMenus="closeAllContextMenus"
|
||||
ref="children"
|
||||
@@ -352,9 +352,9 @@ export default {
|
||||
},
|
||||
collapseAllOtherNodes() {
|
||||
this.$emit('collapseAllOtherNodes');
|
||||
this.$emit('collapseSibbling', this.item);
|
||||
this.$emit('collapseSibling', this.item);
|
||||
},
|
||||
collapseSibbling(item) {
|
||||
collapseSibling(item) {
|
||||
if (!this.$refs.children) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ export default {
|
||||
summarizer(item) {
|
||||
const summary = [];
|
||||
|
||||
if (item.obj.isIncompleteReason) {
|
||||
summary.push({key: 'Incomplete state reason', value: item.obj.isIncompleteReason});
|
||||
if (item.isIncompleteReason) {
|
||||
summary.push({key: 'Incomplete state reason', value: item.isIncompleteReason});
|
||||
}
|
||||
|
||||
return summary;
|
||||
|
||||
36
tools/winscope/src/config/Configuration.json
Normal file
36
tools/winscope/src/config/Configuration.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"invalidProperties": [
|
||||
"length",
|
||||
"prototype",
|
||||
"ref",
|
||||
"diff",
|
||||
"rects",
|
||||
"chips",
|
||||
"parent",
|
||||
"timestamp",
|
||||
"shortName",
|
||||
"kind",
|
||||
"resolvedChildren",
|
||||
"visibilityReason",
|
||||
"absoluteZ",
|
||||
"name",
|
||||
"children",
|
||||
"stableId"
|
||||
],
|
||||
"intDefColumn": {
|
||||
"WindowLayoutParams.type": "android.view.WindowManager.LayoutParams.WindowType",
|
||||
"WindowLayoutParams.flags": "android.view.WindowManager.LayoutParams.Flags",
|
||||
"WindowLayoutParams.privateFlags": "android.view.WindowManager.LayoutParams.PrivateFlags",
|
||||
"WindowLayoutParams.gravity": "android.view.Gravity.GravityFlags",
|
||||
"WindowLayoutParams.softInputMode": "android.view.WindowManager.LayoutParams.WindowType",
|
||||
"WindowLayoutParams.systemUiVisibilityFlags": "android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags",
|
||||
"WindowLayoutParams.subtreeSystemUiVisibilityFlags": "android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags",
|
||||
"WindowLayoutParams.behavior": "android.view.WindowInsetsController.Behavior",
|
||||
"WindowLayoutParams.fitInsetsSides": "android.view.WindowInsets.Side.InsetsSide",
|
||||
"Configuration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
|
||||
"WindowConfiguration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
|
||||
"Configuration.orientation": "android.content.pm.ActivityInfo.ScreenOrientation",
|
||||
"WindowConfiguration.orientation": "android.content.pm.ActivityInfo.ScreenOrientation",
|
||||
"WindowState.orientation": "android.content.pm.ActivityInfo.ScreenOrientation"
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable max-len */
|
||||
|
||||
import jsonProtoDefsAccessibility from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
|
||||
import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
|
||||
import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
|
||||
import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
|
||||
@@ -26,16 +27,16 @@ import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/androi
|
||||
import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
|
||||
import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
|
||||
import protobuf from 'protobufjs';
|
||||
import {transformLayers, transformLayersTrace} from './transform_sf.js';
|
||||
import {transform_accessibility_trace} from './transform_accessibility.js';
|
||||
import {transform_transaction_trace} from './transform_transaction.js';
|
||||
import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
|
||||
import {transformProtolog} from './transform_protolog.js';
|
||||
import {transform_sysui_trace} from './transform_sys_ui.js';
|
||||
import {transform_launcher_trace} from './transform_launcher.js';
|
||||
import {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice} from './transform_ime.js';
|
||||
import {fill_transform_data} from './matrix_utils.js';
|
||||
import {mp4Decoder} from './decodeVideo.js';
|
||||
|
||||
import AccessibilityTrace from '@/traces/Accessibility.ts';
|
||||
import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
|
||||
import WindowManagerTrace from '@/traces/WindowManager.ts';
|
||||
import TransactionsTrace from '@/traces/Transactions.ts';
|
||||
@@ -52,6 +53,7 @@ import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
|
||||
import WindowManagerDump from '@/dumps/WindowManager.ts';
|
||||
import WaylandDump from '@/dumps/Wayland.ts';
|
||||
|
||||
const AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto');
|
||||
const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
|
||||
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
|
||||
const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto');
|
||||
@@ -62,10 +64,11 @@ const WaylandDumpMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.waylan
|
||||
const ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
|
||||
const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto');
|
||||
const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto');
|
||||
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodClientsTraceFileProto");
|
||||
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodServiceTraceFileProto");
|
||||
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodManagerServiceTraceFileProto");
|
||||
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto');
|
||||
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
|
||||
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodManagerServiceTraceFileProto');
|
||||
|
||||
const ACCESSIBILITY_MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
|
||||
const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
|
||||
const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
|
||||
const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
|
||||
@@ -73,11 +76,12 @@ const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x
|
||||
const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
|
||||
const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43]; // .SYSUITRC
|
||||
const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43]; // .LNCHRTRC
|
||||
const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMCTRACE
|
||||
const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMSTRACE
|
||||
const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMMTRACE
|
||||
const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
|
||||
const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
|
||||
const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
|
||||
|
||||
const FILE_TYPES = Object.freeze({
|
||||
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
|
||||
WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
|
||||
SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
|
||||
WINDOW_MANAGER_DUMP: 'WindowManagerDump',
|
||||
@@ -103,8 +107,10 @@ const PROTO_LOG_ICON = 'notes';
|
||||
const SYSTEM_UI_ICON = 'filter_none';
|
||||
const LAUNCHER_ICON = 'filter_none';
|
||||
const IME_ICON = 'keyboard';
|
||||
const ACCESSIBILITY_ICON = 'filter_none';
|
||||
|
||||
const FILE_ICONS = {
|
||||
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
|
||||
[FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON,
|
||||
[FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON,
|
||||
[FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON,
|
||||
@@ -125,11 +131,8 @@ function oneOf(dataType) {
|
||||
return {oneOf: true, type: dataType};
|
||||
}
|
||||
|
||||
function manyOf(dataType, fold = null) {
|
||||
return {manyOf: true, type: dataType, fold};
|
||||
}
|
||||
|
||||
const TRACE_TYPES = Object.freeze({
|
||||
ACCESSIBILITY: 'AccessibilityTrace',
|
||||
WINDOW_MANAGER: 'WindowManagerTrace',
|
||||
SURFACE_FLINGER: 'SurfaceFlingerTrace',
|
||||
SCREEN_RECORDING: 'ScreenRecording',
|
||||
@@ -144,6 +147,12 @@ const TRACE_TYPES = Object.freeze({
|
||||
});
|
||||
|
||||
const TRACE_INFO = {
|
||||
[TRACE_TYPES.ACCESSIBILITY]: {
|
||||
name: 'Accessibility',
|
||||
icon: ACCESSIBILITY_ICON,
|
||||
files: [oneOf(FILE_TYPES.ACCESSIBILITY_TRACE)],
|
||||
constructor: AccessibilityTrace,
|
||||
},
|
||||
[TRACE_TYPES.WINDOW_MANAGER]: {
|
||||
name: 'WindowManager',
|
||||
icon: WINDOW_MANAGER_ICON,
|
||||
@@ -261,6 +270,16 @@ export const TRACE_ICONS = {
|
||||
|
||||
// TODO: Rename name to defaultName
|
||||
const FILE_DECODERS = {
|
||||
[FILE_TYPES.ACCESSIBILITY_TRACE]: {
|
||||
name: 'Accessibility trace',
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.ACCESSIBILITY_TRACE,
|
||||
protoType: AccessibilityTraceMessage,
|
||||
transform: transform_accessibility_trace,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.WINDOW_MANAGER_TRACE]: {
|
||||
name: 'WindowManager trace',
|
||||
decoder: protoDecoder,
|
||||
@@ -278,7 +297,7 @@ const FILE_DECODERS = {
|
||||
type: FILE_TYPES.SURFACE_FLINGER_TRACE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SfTraceMessage,
|
||||
transform: transformLayersTrace,
|
||||
transform: SurfaceFlingerTrace.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
@@ -300,8 +319,8 @@ const FILE_DECODERS = {
|
||||
type: FILE_TYPES.SURFACE_FLINGER_DUMP,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SfDumpMessage,
|
||||
transform: (decoded) => transformLayers(true /* includesCompositionState*/, decoded),
|
||||
timeline: false,
|
||||
transform: SurfaceFlingerDump.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.WINDOW_MANAGER_DUMP]: {
|
||||
@@ -312,7 +331,7 @@ const FILE_DECODERS = {
|
||||
mime: 'application/octet-stream',
|
||||
protoType: WmDumpMessage,
|
||||
transform: WindowManagerDump.fromProto,
|
||||
timeline: false,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.WAYLAND_DUMP]: {
|
||||
@@ -323,7 +342,7 @@ const FILE_DECODERS = {
|
||||
mime: 'application/octet-stream',
|
||||
protoType: WaylandDumpMessage,
|
||||
transform: transform_wl_outputstate,
|
||||
timeline: false,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.SCREEN_RECORDING]: {
|
||||
@@ -443,11 +462,6 @@ function modifyProtoFields(protoObj, displayDefaults) {
|
||||
protoObj[fieldName] = fieldProperties.defaultValue;
|
||||
}
|
||||
|
||||
if (fieldProperties.type === 'TransformProto') {
|
||||
fill_transform_data(protoObj[fieldName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
|
||||
protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
|
||||
continue;
|
||||
@@ -524,6 +538,9 @@ function detectAndDecode(buffer, fileName, store) {
|
||||
if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, ACCESSIBILITY_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.ACCESSIBILITY_TRACE, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
|
||||
import DumpBase from "./DumpBase";
|
||||
import LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry'
|
||||
|
||||
export default class SurfaceFlinger extends DumpBase {
|
||||
sfDumpFile: any;
|
||||
@@ -30,4 +31,12 @@ export default class SurfaceFlinger extends DumpBase {
|
||||
get type() {
|
||||
return DUMP_TYPES.SURFACE_FLINGER;
|
||||
}
|
||||
|
||||
static fromProto(proto: any): LayersTraceEntry {
|
||||
return LayersTraceEntry.fromProto(
|
||||
/*protos */ proto.layers,
|
||||
/* timestamp */ 0,
|
||||
/* hwcBlob */ ""
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export default class WindowManager extends DumpBase {
|
||||
return DUMP_TYPES.WINDOW_MANAGER;
|
||||
}
|
||||
|
||||
static fromProto(proto): WindowManagerTrace {
|
||||
static fromProto(proto: any): WindowManagerTrace {
|
||||
return WindowManagerTrace.fromDump(proto);
|
||||
}
|
||||
}
|
||||
35
tools/winscope/src/flickerlib/LayersTrace.ts
Normal file
35
tools/winscope/src/flickerlib/LayersTrace.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { LayersTrace } from "./common"
|
||||
import LayerTraceEntry from './layers/LayerTraceEntry'
|
||||
|
||||
LayersTrace.fromProto = function (proto: any): LayersTrace {
|
||||
const entries = []
|
||||
for (const entryProto of proto.entry) {
|
||||
const transformedEntry = LayerTraceEntry.fromProto(
|
||||
/* protos */ entryProto.layers.layers,
|
||||
/* timestamp */ entryProto.elapsedRealtimeNanos,
|
||||
/* hwcBlob */ entryProto.hwcBlob);
|
||||
|
||||
entries.push(transformedEntry);
|
||||
}
|
||||
const source = null;
|
||||
const trace = new LayersTrace(entries, source);
|
||||
return trace;
|
||||
}
|
||||
|
||||
export default LayersTrace;
|
||||
@@ -14,48 +14,84 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {toBounds, toBuffer, toColor, toPoint, toRect,
|
||||
import {toSize, toBuffer, toColor, toPoint, toRect,
|
||||
toRectF, toRegion, toTransform} from './common';
|
||||
import intDefMapping from
|
||||
'../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
|
||||
import config from '../config/Configuration.json'
|
||||
|
||||
function readIntdefMap(): Map<string, string> {
|
||||
const map = new Map<string, string>();
|
||||
const keys = Object.keys(config.intDefColumn);
|
||||
|
||||
keys.forEach(key => {
|
||||
const value = config.intDefColumn[key];
|
||||
map.set(key, value);
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
export default class ObjectFormatter {
|
||||
private static INVALID_ELEMENT_PROPERTIES = ['length', 'name', 'prototype', 'children',
|
||||
'childrenWindows', 'ref', 'root', 'layers', 'resolvedChildren']
|
||||
static displayDefaults: boolean = false
|
||||
private static INVALID_ELEMENT_PROPERTIES = config.invalidProperties;
|
||||
|
||||
private static FLICKER_INTDEF_MAP = new Map([
|
||||
[`WindowLayoutParams.type`, `android.view.WindowManager.LayoutParams.WindowType`],
|
||||
[`WindowLayoutParams.flags`, `android.view.WindowManager.LayoutParams.Flags`],
|
||||
[`WindowLayoutParams.privateFlags`, `android.view.WindowManager.LayoutParams.PrivateFlags`],
|
||||
[`WindowLayoutParams.gravity`, `android.view.Gravity.GravityFlags`],
|
||||
[`WindowLayoutParams.softInputMode`, `android.view.WindowManager.LayoutParams.WindowType`],
|
||||
[`WindowLayoutParams.systemUiVisibilityFlags`, `android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags`],
|
||||
[`WindowLayoutParams.subtreeSystemUiVisibilityFlags`, `android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags`],
|
||||
[`WindowLayoutParams.behavior`, `android.view.WindowInsetsController.Behavior`],
|
||||
[`WindowLayoutParams.fitInsetsSides`, `android.view.WindowInsets.Side.InsetsSide`],
|
||||
private static FLICKER_INTDEF_MAP = readIntdefMap();
|
||||
|
||||
[`Configuration.windowingMode`, `android.app.WindowConfiguration.WindowingMode`],
|
||||
[`WindowConfiguration.windowingMode`, `android.app.WindowConfiguration.WindowingMode`],
|
||||
[`Configuration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
|
||||
[`WindowConfiguration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
|
||||
[`WindowState.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
|
||||
])
|
||||
static cloneObject(entry: any): any {
|
||||
let obj: any = {}
|
||||
const properties = ObjectFormatter.getProperties(entry);
|
||||
properties.forEach(prop => obj[prop] = entry[prop]);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static getProperties(entry: any): string[] {
|
||||
var props = [];
|
||||
let obj = entry;
|
||||
|
||||
do {
|
||||
const properties = Object.getOwnPropertyNames(obj).filter(it => {
|
||||
// filter out functions
|
||||
if (typeof(entry[it]) === 'function') return false;
|
||||
// internal propertires from kotlinJs
|
||||
if (it.includes(`$`)) return false;
|
||||
// private kotlin variables from kotlin
|
||||
if (it.startsWith(`_`)) return false;
|
||||
// some predefined properties used only internally (e.g., children, ref, diff)
|
||||
if (this.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
|
||||
// Flicker object properties or arrays
|
||||
if (!entry[it]) return false;
|
||||
const value = entry[it]
|
||||
// only non-empty arrays of non-flicker objects (otherwise they are in hierarchy)
|
||||
if (Array.isArray(value) && value.length > 0) return !value[0].stableId
|
||||
// non-flicker object
|
||||
return !value.stableId;
|
||||
});
|
||||
properties.forEach(function (prop) {
|
||||
if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
|
||||
props.push(prop);
|
||||
}
|
||||
});
|
||||
} while (obj = Object.getPrototypeOf(obj));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static format(obj: any): {} {
|
||||
const entries = Object.entries(obj)
|
||||
.filter(it => !it[0].includes(`$`))
|
||||
.filter(it => !this.INVALID_ELEMENT_PROPERTIES.includes(it[0]))
|
||||
const sortedEntries = entries.sort()
|
||||
const properties = this.getProperties(obj);
|
||||
const sortedProperties = properties.sort()
|
||||
|
||||
const result: any = {}
|
||||
sortedEntries.forEach(entry => {
|
||||
const key = entry[0]
|
||||
const value: any = entry[1]
|
||||
sortedProperties.forEach(entry => {
|
||||
const key = entry;
|
||||
const value: any = obj[key];
|
||||
|
||||
if (value) {
|
||||
if (value || (this.displayDefaults && value !== undefined && value !== null)) {
|
||||
// flicker obj
|
||||
if (value.prettyPrint) {
|
||||
const isEmpty = value.isEmpty === true;
|
||||
if (!isEmpty || this.displayDefaults) {
|
||||
result[key] = value.prettyPrint()
|
||||
}
|
||||
} else {
|
||||
// converted proto to flicker
|
||||
const translatedObject = this.translateObject(value)
|
||||
@@ -63,7 +99,11 @@ export default class ObjectFormatter {
|
||||
result[key] = translatedObject.prettyPrint()
|
||||
// objects - recursive call
|
||||
} else if (value && typeof(value) == `object`) {
|
||||
result[key] = this.format(value)
|
||||
const childObj = this.format(value) as any
|
||||
const isEmpty = Object.entries(childObj).length == 0 || childObj.isEmpty
|
||||
if (!isEmpty || this.displayDefaults) {
|
||||
result[key] = childObj
|
||||
}
|
||||
} else {
|
||||
// values
|
||||
result[key] = this.translateIntDef(obj, key, value)
|
||||
@@ -73,7 +113,8 @@ export default class ObjectFormatter {
|
||||
}
|
||||
})
|
||||
|
||||
return Object.freeze(result)
|
||||
// return Object.freeze(result)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +127,7 @@ export default class ObjectFormatter {
|
||||
private static translateObject(obj) {
|
||||
const type = obj?.$type?.name
|
||||
switch(type) {
|
||||
case `SizeProto`: return toBounds(obj)
|
||||
case `SizeProto`: return toSize(obj)
|
||||
case `ActiveBufferProto`: return toBuffer(obj)
|
||||
case `ColorProto`: return toColor(obj)
|
||||
case `PointProto`: return toPoint(obj)
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { asRawTreeViewObject } from '../utils/diff.js'
|
||||
import { getPropertiesForDisplay } from './mixin'
|
||||
|
||||
import {
|
||||
KeyguardControllerState,
|
||||
RootWindowContainer,
|
||||
@@ -26,17 +23,20 @@ import {
|
||||
|
||||
import WindowContainer from "./windows/WindowContainer"
|
||||
|
||||
WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): WindowManagerState {
|
||||
var inputMethodWIndowAppToken = ""
|
||||
WindowManagerState.fromProto = function (proto: any, timestamp: number = 0, where: string = ""): WindowManagerState {
|
||||
var inputMethodWIndowAppToken = "";
|
||||
if (proto.inputMethodWindow != null) {
|
||||
proto.inputMethodWindow.hashCode.toString(16)
|
||||
}
|
||||
const rootWindowContainer = newRootWindowContainer(proto.rootWindowContainer)
|
||||
const keyguardControllerState = newKeyguardControllerState(
|
||||
proto.rootWindowContainer.keyguardController)
|
||||
};
|
||||
|
||||
const rootWindowContainer = createRootWindowContainer(proto.rootWindowContainer);
|
||||
const keyguardControllerState = createKeyguardControllerState(
|
||||
proto.rootWindowContainer.keyguardController);
|
||||
const policy = createWindowManagerPolicy(proto.policy);
|
||||
|
||||
const entry = new WindowManagerState(
|
||||
where,
|
||||
newWindowManagerPolicy(proto.policy),
|
||||
policy,
|
||||
proto.focusedApp,
|
||||
proto.focusedDisplayId,
|
||||
proto.focusedWindow?.title ?? "",
|
||||
@@ -47,26 +47,30 @@ WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): Wi
|
||||
rootWindowContainer,
|
||||
keyguardControllerState,
|
||||
timestamp = timestamp
|
||||
)
|
||||
|
||||
entry.kind = entry.constructor.name
|
||||
entry.rects = entry.windowStates.reverse().map(it => it.rect)
|
||||
if (!entry.isComplete()) {
|
||||
entry.isIncompleteReason = entry.getIsIncompleteReason()
|
||||
}
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.shortName = entry.name
|
||||
entry.chips = []
|
||||
entry.visible = true
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
);
|
||||
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
}
|
||||
|
||||
function newWindowManagerPolicy(proto): WindowManagerPolicy {
|
||||
function addAttributes(entry: WindowManagerState, proto: any) {
|
||||
entry.kind = entry.constructor.name;
|
||||
// There no JVM/JS translation for Longs yet
|
||||
entry.timestampMs = entry.timestamp.toString();
|
||||
entry.rects = entry.windowStates.reverse().map(it => it.rect);
|
||||
if (!entry.isComplete()) {
|
||||
entry.isIncompleteReason = entry.getIsIncompleteReason();
|
||||
}
|
||||
entry.proto = proto;
|
||||
entry.shortName = entry.name;
|
||||
entry.chips = [];
|
||||
entry.isVisible = true;
|
||||
}
|
||||
|
||||
function createWindowManagerPolicy(proto: any): WindowManagerPolicy {
|
||||
return new WindowManagerPolicy(
|
||||
proto.focusedAppToken || "",
|
||||
proto.focusedAppToken ?? "",
|
||||
proto.forceStatusBar,
|
||||
proto.forceStatusBarFromKeyguard,
|
||||
proto.keyguardDrawComplete,
|
||||
@@ -79,36 +83,35 @@ function newWindowManagerPolicy(proto): WindowManagerPolicy {
|
||||
proto.rotationMode,
|
||||
proto.screenOnFully,
|
||||
proto.windowManagerDrawComplete
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function newRootWindowContainer(proto): RootWindowContainer {
|
||||
const children = proto.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ false))
|
||||
function createRootWindowContainer(proto: any): RootWindowContainer {
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
{proto: proto.windowContainer, children: children})
|
||||
/* proto */ proto.windowContainer,
|
||||
/* childrenProto */ proto.windowContainer.children.reverse()
|
||||
);
|
||||
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
throw new Error(`Window container should not be null.\n${JSON.stringify(proto)}`);
|
||||
}
|
||||
const entry = new RootWindowContainer(windowContainer)
|
||||
|
||||
return entry
|
||||
const entry = new RootWindowContainer(windowContainer);
|
||||
return entry;
|
||||
}
|
||||
|
||||
function newKeyguardControllerState(proto): KeyguardControllerState {
|
||||
const keyguardOccludedStates = {}
|
||||
function createKeyguardControllerState(proto: any): KeyguardControllerState {
|
||||
const keyguardOccludedStates = {};
|
||||
|
||||
if (proto) {
|
||||
proto.keyguardOccludedStates.forEach(it =>
|
||||
keyguardOccludedStates[it.displayId] = it.keyguardOccluded)
|
||||
keyguardOccludedStates[it.displayId] = it.keyguardOccluded);
|
||||
}
|
||||
|
||||
return new KeyguardControllerState(
|
||||
proto?.isAodShowing ?? false,
|
||||
proto?.isKeyguardShowing ?? false,
|
||||
keyguardOccludedStates
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default WindowManagerState;
|
||||
@@ -17,23 +17,22 @@
|
||||
import { WindowManagerTrace } from "./common"
|
||||
import WindowManagerState from "./WindowManagerState"
|
||||
|
||||
WindowManagerTrace.fromProto = function (proto) {
|
||||
const entries = []
|
||||
WindowManagerTrace.fromProto = function (proto: any) {
|
||||
const entries = [];
|
||||
for (const entryProto of proto.entry) {
|
||||
const transformedEntry = WindowManagerState.fromProto({
|
||||
proto: entryProto.windowManagerService,
|
||||
timestamp: entryProto.elapsedRealtimeNanos,
|
||||
where: entryProto.where})
|
||||
const transformedEntry = WindowManagerState.fromProto(
|
||||
entryProto.windowManagerService,
|
||||
entryProto.elapsedRealtimeNanos,
|
||||
entryProto.where);
|
||||
|
||||
entries.push(transformedEntry)
|
||||
entries.push(transformedEntry);
|
||||
}
|
||||
const source = null
|
||||
const sourceChecksum = null
|
||||
return new WindowManagerTrace(entries, source, sourceChecksum)
|
||||
const source = null;
|
||||
return new WindowManagerTrace(entries, source);
|
||||
}
|
||||
|
||||
WindowManagerTrace.fromDump = function(proto): WindowManagerTrace {
|
||||
return WindowManagerState.fromProto({proto: proto})
|
||||
WindowManagerTrace.fromDump = function(proto: any): WindowManagerTrace {
|
||||
return WindowManagerState.fromProto(proto);
|
||||
}
|
||||
|
||||
export default WindowManagerTrace;
|
||||
|
||||
@@ -17,46 +17,58 @@
|
||||
// Imports all the compiled common Flicker library classes and exports them
|
||||
// as clean es6 modules rather than having them be commonjs modules
|
||||
|
||||
const WindowManagerTrace = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.WindowManagerTrace;
|
||||
const WindowManagerState = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.WindowManagerState;
|
||||
|
||||
// WM
|
||||
const WindowManagerTrace = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.WindowManagerTrace;
|
||||
const WindowManagerState = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.WindowManagerState;
|
||||
const Activity = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.Activity;
|
||||
const Configuration = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.Configuration;
|
||||
const ConfigurationContainer = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.ConfigurationContainer;
|
||||
const ConfigurationContainer = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.windows.ConfigurationContainer;
|
||||
const DisplayArea = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.DisplayArea;
|
||||
const DisplayContent = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.DisplayContent;
|
||||
const KeyguardControllerState = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.KeyguardControllerState;
|
||||
const RootWindowContainer = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.RootWindowContainer;
|
||||
const KeyguardControllerState = require('flicker').com.android.server.wm.
|
||||
traces.common.windowmanager.windows.KeyguardControllerState;
|
||||
const RootWindowContainer = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.windows.RootWindowContainer;
|
||||
const Task = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.Task;
|
||||
const TaskFragment = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.TaskFragment;
|
||||
const WindowConfiguration = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowConfiguration;
|
||||
const WindowConfiguration = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.windows.WindowConfiguration;
|
||||
const WindowContainer = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowContainer;
|
||||
const WindowLayoutParams= require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowLayoutParams;
|
||||
const WindowManagerPolicy = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowManagerPolicy;
|
||||
const WindowLayoutParams= require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.windows.WindowLayoutParams;
|
||||
const WindowManagerPolicy = require('flicker').com.android.server.wm.traces.
|
||||
common.windowmanager.windows.WindowManagerPolicy;
|
||||
const WindowState = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowState;
|
||||
const WindowToken = require('flicker').com.android.server.wm.traces.common.
|
||||
windowmanager.windows.WindowToken;
|
||||
|
||||
const Matrix = require('flicker').com.android.server.wm.traces.common.layers.Transform.Matrix;
|
||||
const Transform = require('flicker').com.android.server.wm.traces.common.layers.Transform;
|
||||
// SF
|
||||
const Layer = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.Layer;
|
||||
const LayerTraceEntry = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.LayerTraceEntry;
|
||||
const LayerTraceEntryBuilder = require('flicker').com.android.server.wm.traces.
|
||||
common.layers.LayerTraceEntryBuilder;
|
||||
const LayersTrace = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.LayersTrace;
|
||||
const Matrix = require('flicker').com.android.server.wm.traces.common.layers.
|
||||
Transform.Matrix;
|
||||
const Transform = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.Transform;
|
||||
|
||||
const Bounds = require('flicker').com.android.server.wm.traces.common.Bounds;
|
||||
// Common
|
||||
const Size = require('flicker').com.android.server.wm.traces.common.Size;
|
||||
const Buffer = require('flicker').com.android.server.wm.traces.common.Buffer;
|
||||
const Color = require('flicker').com.android.server.wm.traces.common.Color;
|
||||
const Point = require('flicker').com.android.server.wm.traces.common.Point;
|
||||
@@ -64,70 +76,129 @@ const Rect = require('flicker').com.android.server.wm.traces.common.Rect;
|
||||
const RectF = require('flicker').com.android.server.wm.traces.common.RectF;
|
||||
const Region = require('flicker').com.android.server.wm.traces.common.Region;
|
||||
|
||||
function toBounds(proto) {
|
||||
const EMPTY_BUFFER = new Buffer(0, 0, 0, 0);
|
||||
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
|
||||
const EMPTY_RECT = new Rect(0, 0, 0, 0);
|
||||
const EMPTY_RECTF = new RectF(0, 0, 0, 0);
|
||||
const EMPTY_POINT = new Point(0, 0);
|
||||
const EMPTY_MATRIX = new Matrix(0, 0, 0, 0, 0, 0);
|
||||
const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX);
|
||||
|
||||
function toSize(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return EMPTY_BOUNDS;
|
||||
}
|
||||
return new Bounds(proto.width ?? proto.w ?? 0, proto.height ?? proto.h ?? 0)
|
||||
const width = proto.width ?? proto.w ?? 0;
|
||||
const height = proto.height ?? proto.h ?? 0;
|
||||
if (width || height) {
|
||||
return new Size(width, height);
|
||||
}
|
||||
return EMPTY_BOUNDS;
|
||||
}
|
||||
|
||||
function toBuffer(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
const width = proto?.width ?? 0;
|
||||
const height = proto?.height ?? 0;
|
||||
const stride = proto?.stride ?? 0;
|
||||
const format = proto?.format ?? 0;
|
||||
|
||||
if (width || height || stride || format) {
|
||||
return new Buffer(width, height, stride, format);
|
||||
}
|
||||
return new Buffer(proto.width ?? 0, proto.height ?? 0, proto.stride ?? 0, proto.format ?? 0)
|
||||
return EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
function toColor(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return EMPTY_COLOR;
|
||||
}
|
||||
return new Color(proto.r ?? 0, proto.g ?? 0, proto.b ?? 0, proto.a ?? 0)
|
||||
const r = proto.r ?? 0;
|
||||
const g = proto.g ?? 0;
|
||||
const b = proto.b ?? 0;
|
||||
const a = proto.a ?? 0;
|
||||
if (r || g || b || a) {
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
return EMPTY_COLOR;
|
||||
}
|
||||
|
||||
function toPoint(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
return new Point(proto.x ?? 0, proto.y ?? 0)
|
||||
const x = proto.x ?? 0;
|
||||
const y = proto.y ?? 0;
|
||||
if (x || y) {
|
||||
return new Point(x, y);
|
||||
}
|
||||
return EMPTY_POINT;
|
||||
}
|
||||
|
||||
function toRect(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return EMPTY_RECT;
|
||||
}
|
||||
return new Rect(proto.left ?? 0, proto.top ?? 0, proto.right ?? 0, proto.bottom ?? 0)
|
||||
|
||||
const left = proto?.left ?? 0;
|
||||
const top = proto?.top ?? 0;
|
||||
const right = proto?.right ?? 0;
|
||||
const bottom = proto?.bottom ?? 0;
|
||||
if (left || top || right || bottom) {
|
||||
return new Rect(left, top, right, bottom);
|
||||
}
|
||||
return EMPTY_RECT;
|
||||
}
|
||||
|
||||
function toRectF(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return EMPTY_RECTF;
|
||||
}
|
||||
return new RectF(proto.left ?? 0, proto.top ?? 0, proto.right ?? 0, proto.bottom ?? 0)
|
||||
|
||||
const left = proto?.left ?? 0;
|
||||
const top = proto?.top ?? 0;
|
||||
const right = proto?.right ?? 0;
|
||||
const bottom = proto?.bottom ?? 0;
|
||||
if (left || top || right || bottom) {
|
||||
return new RectF(left, top, right, bottom);
|
||||
}
|
||||
return EMPTY_RECTF;
|
||||
}
|
||||
|
||||
function toRegion(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
let rects = []
|
||||
for (let rectNr in proto.rect) {
|
||||
const rect = proto.rect[rectNr]
|
||||
const parsedRect = toRect(rect)
|
||||
rects.push(parsedRect)
|
||||
const rects = [];
|
||||
for (let x = 0; x < proto.rect.length; x++) {
|
||||
const rect = proto.rect[x];
|
||||
const parsedRect = toRect(rect);
|
||||
rects.push(parsedRect);
|
||||
}
|
||||
|
||||
return new Region(rects)
|
||||
return new Region(rects);
|
||||
}
|
||||
|
||||
function toTransform(proto) {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return EMPTY_TRANSFORM;
|
||||
}
|
||||
const matrix = new Matrix(proto.dsdx ?? 0, proto.dtdx ?? 0,
|
||||
proto.tx ?? 0, proto.dsdy ?? 0, proto.dtdy ?? 0, proto.ty ?? 0)
|
||||
return new Transform(proto.type ?? 0, matrix)
|
||||
const dsdx = proto.dsdx ?? 0;
|
||||
const dtdx = proto.dtdx ?? 0;
|
||||
const tx = proto.tx ?? 0;
|
||||
const dsdy = proto.dsdy ?? 0;
|
||||
const dtdy = proto.dtdy ?? 0;
|
||||
const ty = proto.ty ?? 0;
|
||||
|
||||
if (dsdx || dtdx || tx || dsdy || dtdy || ty) {
|
||||
const matrix = new Matrix(dsdx, dtdx, tx, dsdy, dtdy, ty);
|
||||
return new Transform(proto.type ?? 0, matrix);
|
||||
}
|
||||
|
||||
if (proto.type) {
|
||||
return new Transform(proto.type ?? 0, EMPTY_MATRIX);
|
||||
}
|
||||
return EMPTY_TRANSFORM;
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -148,19 +219,27 @@ export {
|
||||
WindowManagerPolicy,
|
||||
WindowManagerTrace,
|
||||
WindowManagerState,
|
||||
Bounds,
|
||||
// SF
|
||||
Layer,
|
||||
LayerTraceEntry,
|
||||
LayerTraceEntryBuilder,
|
||||
LayersTrace,
|
||||
Transform,
|
||||
Matrix,
|
||||
// Common
|
||||
Size,
|
||||
Buffer,
|
||||
Color,
|
||||
Point,
|
||||
Rect,
|
||||
RectF,
|
||||
Region,
|
||||
toBounds,
|
||||
toSize,
|
||||
toBuffer,
|
||||
toColor,
|
||||
toPoint,
|
||||
toRect,
|
||||
toRectF,
|
||||
toRegion,
|
||||
toTransform
|
||||
toTransform,
|
||||
};
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LayersTrace from './LayersTrace';
|
||||
import WindowManagerState from './WindowManagerState';
|
||||
import WindowManagerTrace from './WindowManagerTrace';
|
||||
|
||||
import ObjectFormatter from './ObjectFormatter';
|
||||
/**
|
||||
* Entry point into the flickerlib for Winscope.
|
||||
* Expose everything we want Winscope to have access to here.
|
||||
*/
|
||||
export {WindowManagerState, WindowManagerTrace};
|
||||
export {ObjectFormatter, LayersTrace, WindowManagerState, WindowManagerTrace};
|
||||
|
||||
|
||||
95
tools/winscope/src/flickerlib/layers/Layer.ts
Normal file
95
tools/winscope/src/flickerlib/layers/Layer.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2021, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import { Layer, Rect, toBuffer, toColor, toRect, toRectF, toRegion } from "../common"
|
||||
import { shortenName } from '../mixin'
|
||||
import { RELATIVE_Z_CHIP, GPU_CHIP, HWC_CHIP } from '../treeview/Chips'
|
||||
import Transform from './Transform'
|
||||
|
||||
Layer.fromProto = function (proto: any): Layer {
|
||||
const visibleRegion = toRegion(proto.visibleRegion)
|
||||
const activeBuffer = toBuffer(proto.activeBuffer)
|
||||
const bounds = toRectF(proto.bounds)
|
||||
const color = toColor(proto.color)
|
||||
const screenBounds = toRectF(proto.screenBounds)
|
||||
const sourceBounds = toRectF(proto.sourceBounds)
|
||||
const transform = Transform.fromProto(proto.transform, proto.position)
|
||||
const bufferTransform = Transform.fromProto(proto.bufferTransform, /* position */ null)
|
||||
const hwcCrop = toRectF(proto.hwcCrop)
|
||||
const hwcFrame = toRect(proto.hwcFrame)
|
||||
let crop: Rect
|
||||
if (proto.crop) {
|
||||
crop = toRect(proto.crop)
|
||||
};
|
||||
|
||||
const entry = new Layer(
|
||||
proto.name ?? ``,
|
||||
proto.id,
|
||||
proto.parent,
|
||||
proto.z,
|
||||
visibleRegion,
|
||||
activeBuffer,
|
||||
proto.flags,
|
||||
bounds,
|
||||
color,
|
||||
proto.isOpaque,
|
||||
proto.shadowRadius,
|
||||
proto.cornerRadius,
|
||||
proto.type ?? ``,
|
||||
screenBounds,
|
||||
transform,
|
||||
sourceBounds,
|
||||
proto.currFrame,
|
||||
proto.effectiveScalingMode,
|
||||
bufferTransform,
|
||||
proto.hwcCompositionType,
|
||||
hwcCrop,
|
||||
hwcFrame,
|
||||
proto.backgroundBlurRadius,
|
||||
crop,
|
||||
proto.isRelativeOf,
|
||||
proto.zOrderRelativeOf
|
||||
);
|
||||
|
||||
addAttributes(entry, proto);
|
||||
return entry
|
||||
}
|
||||
|
||||
function addAttributes(entry: Layer, proto: any) {
|
||||
entry.kind = `${entry.id}`;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
entry.proto = proto;
|
||||
entry.rect = entry.bounds;
|
||||
entry.rect.transform = entry.transform;
|
||||
entry.rect.ref = entry;
|
||||
entry.rect.label = entry.name;
|
||||
entry.chips = [];
|
||||
updateChips(entry);
|
||||
}
|
||||
|
||||
function updateChips(entry) {
|
||||
if ((entry.zOrderRelativeOf || -1) !== -1) {
|
||||
entry.chips.push(RELATIVE_Z_CHIP);
|
||||
}
|
||||
if (entry.hwcCompositionType === 'CLIENT') {
|
||||
entry.chips.push(GPU_CHIP);
|
||||
} else if (entry.hwcCompositionType === 'DEVICE' || entry.hwcCompositionType === 'SOLID_COLOR') {
|
||||
entry.chips.push(HWC_CHIP);
|
||||
}
|
||||
}
|
||||
|
||||
export default Layer;
|
||||
66
tools/winscope/src/flickerlib/layers/LayerTraceEntry.ts
Normal file
66
tools/winscope/src/flickerlib/layers/LayerTraceEntry.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { LayerTraceEntry, LayerTraceEntryBuilder } from "../common"
|
||||
import Layer from './Layer'
|
||||
import { VISIBLE_CHIP, RELATIVE_Z_PARENT_CHIP, MISSING_LAYER } from '../treeview/Chips'
|
||||
|
||||
LayerTraceEntry.fromProto = function (protos: any[], timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
|
||||
const layers = protos.map(it => Layer.fromProto(it));
|
||||
const builder = new LayerTraceEntryBuilder(timestamp, layers, hwcBlob, where);
|
||||
const entry: LayerTraceEntry = builder.build();
|
||||
|
||||
updateChildren(entry);
|
||||
addAttributes(entry, protos);
|
||||
return entry;
|
||||
}
|
||||
|
||||
function addAttributes(entry: LayerTraceEntry, protos: any) {
|
||||
entry.kind = "entry"
|
||||
// There no JVM/JS translation for Longs yet
|
||||
entry.timestampMs = entry.timestamp.toString()
|
||||
entry.rects = entry.visibleLayers
|
||||
.sort((a, b) => (b.absoluteZ > a.absoluteZ) ? 1 : (a.absoluteZ == b.absoluteZ) ? 0 : -1)
|
||||
.map(it => it.rect);
|
||||
|
||||
// Avoid parsing the entry root because it is an array of layers
|
||||
// containing all trace information, this slows down the property tree.
|
||||
// Instead parse only key properties for debugging
|
||||
const entryIds = {}
|
||||
protos.forEach(it =>
|
||||
entryIds[it.id] = `\nparent=${it.parent}\ntype=${it.type}\nname=${it.name}`
|
||||
);
|
||||
entry.proto = entryIds;
|
||||
entry.shortName = entry.name;
|
||||
entry.chips = [];
|
||||
entry.isVisible = true;
|
||||
}
|
||||
|
||||
function updateChildren(entry: LayerTraceEntry) {
|
||||
entry.flattenedLayers.forEach(it => {
|
||||
if (it.isVisible) {
|
||||
it.chips.push(VISIBLE_CHIP);
|
||||
}
|
||||
if (it.zOrderRelativeOf) {
|
||||
it.chips.push(RELATIVE_Z_PARENT_CHIP);
|
||||
}
|
||||
if (it.isMissing) {
|
||||
it.chips.push(MISSING_LAYER);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default LayerTraceEntry;
|
||||
90
tools/winscope/src/flickerlib/layers/Transform.ts
Normal file
90
tools/winscope/src/flickerlib/layers/Transform.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2021, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Transform, Matrix } from "../common"
|
||||
|
||||
Transform.fromProto = function (transformProto, positionProto): Transform {
|
||||
const entry = new Transform(
|
||||
transformProto?.type ?? 0,
|
||||
getMatrix(transformProto, positionProto))
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
function getMatrix(transform, position): Matrix {
|
||||
const x = position?.x ?? 0
|
||||
const y = position?.y ?? 0
|
||||
|
||||
if (transform == null || isSimpleTransform(transform.type)) {
|
||||
return getDefaultTransform(transform?.type, x, y)
|
||||
}
|
||||
|
||||
return new Matrix(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
|
||||
}
|
||||
|
||||
function getDefaultTransform(type, x, y): Matrix {
|
||||
// IDENTITY
|
||||
if (!type) {
|
||||
return new Matrix(1, 0, x, 0, 1, y)
|
||||
}
|
||||
|
||||
// ROT_270 = ROT_90|FLIP_H|FLIP_V
|
||||
if (isFlagSet(type, ROT_90_VAL | FLIP_V_VAL | FLIP_H_VAL)) {
|
||||
return new Matrix(0, -1, x, 1, 0, y)
|
||||
}
|
||||
|
||||
// ROT_180 = FLIP_H|FLIP_V
|
||||
if (isFlagSet(type, FLIP_V_VAL | FLIP_H_VAL)) {
|
||||
return new Matrix(-1, 0, x, 0, -1, y)
|
||||
}
|
||||
|
||||
// ROT_90
|
||||
if (isFlagSet(type, ROT_90_VAL)) {
|
||||
return new Matrix(0, 1, x, -1, 0, y)
|
||||
}
|
||||
|
||||
// IDENTITY
|
||||
if (isFlagClear(type, SCALE_VAL | ROTATE_VAL)) {
|
||||
return new Matrix(1, 0, x, 0, 1, y)
|
||||
}
|
||||
|
||||
throw new Error(`Unknown transform type ${type}`)
|
||||
}
|
||||
|
||||
export function isFlagSet(type, bits): Boolean {
|
||||
var type = type || 0;
|
||||
return (type & bits) === bits;
|
||||
}
|
||||
|
||||
export function isFlagClear(type, bits): Boolean {
|
||||
return (type & bits) === 0;
|
||||
}
|
||||
|
||||
export function isSimpleTransform(type): Boolean {
|
||||
return isFlagClear(type, ROT_INVALID_VAL | SCALE_VAL)
|
||||
}
|
||||
|
||||
/* transform type flags */
|
||||
const ROTATE_VAL = 0x0002
|
||||
const SCALE_VAL = 0x0004
|
||||
|
||||
/* orientation flags */
|
||||
const FLIP_H_VAL = 0x0100 // (1 << 0 << 8)
|
||||
const FLIP_V_VAL = 0x0200 // (1 << 1 << 8)
|
||||
const ROT_90_VAL = 0x0400 // (1 << 2 << 8)
|
||||
const ROT_INVALID_VAL = 0x8000 // (0x80 << 8)
|
||||
|
||||
export default Transform
|
||||
@@ -22,12 +22,22 @@ import ObjectFormatter from "./ObjectFormatter"
|
||||
* @param entry WM hierarchy element
|
||||
* @param proto Associated proto object
|
||||
*/
|
||||
export function getPropertiesForDisplay(proto: any, entry: any): any {
|
||||
let obj = Object.assign({}, entry)
|
||||
if (obj.children) delete obj.children
|
||||
// obj = ObjectFormatter.format(obj)
|
||||
export function getPropertiesForDisplay(entry: any): any {
|
||||
if (!entry) {
|
||||
return
|
||||
}
|
||||
|
||||
obj.proto = Object.assign({}, proto)
|
||||
let obj: any = {}
|
||||
const properties = ObjectFormatter.getProperties(entry)
|
||||
properties.forEach(prop => obj[prop] = entry[prop]);
|
||||
|
||||
// we remove the children property from the object to avoid it showing the
|
||||
// the properties view of the element as we can always see those elements'
|
||||
// properties by changing the target element in the hierarchy tree view.
|
||||
if (obj.children) delete obj.children
|
||||
if (obj.proto) delete obj.proto
|
||||
|
||||
obj.proto = Object.assign({}, entry.proto)
|
||||
if (obj.proto.children) delete obj.proto.children
|
||||
if (obj.proto.childWindows) delete obj.proto.childWindows
|
||||
if (obj.proto.childrenWindows) delete obj.proto.childrenWindows
|
||||
|
||||
@@ -18,3 +18,33 @@ import Chip from "./Chip"
|
||||
import ChipType from "./ChipType"
|
||||
|
||||
export const VISIBLE_CHIP = new Chip("V", "visible", ChipType.DEFAULT)
|
||||
|
||||
export const RELATIVE_Z_CHIP = {
|
||||
short: 'RelZ',
|
||||
long: 'Is relative Z-ordered to another surface',
|
||||
class: 'warn',
|
||||
};
|
||||
|
||||
export const RELATIVE_Z_PARENT_CHIP = {
|
||||
short: 'RelZParent',
|
||||
long: 'Something is relative Z-ordered to this surface',
|
||||
class: 'warn',
|
||||
};
|
||||
|
||||
export const MISSING_LAYER = {
|
||||
short: 'MissingLayer',
|
||||
long: 'This layer was referenced from the parent, but not present in the trace',
|
||||
class: 'error',
|
||||
};
|
||||
|
||||
export const GPU_CHIP = {
|
||||
short: 'GPU',
|
||||
long: 'This layer was composed on the GPU',
|
||||
class: 'gpu',
|
||||
};
|
||||
|
||||
export const HWC_CHIP = {
|
||||
short: 'HWC',
|
||||
long: 'This layer was composed by Hardware Composer',
|
||||
class: 'hwc',
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import Chip from "./Chip"
|
||||
import { TreeViewObject } from "./types"
|
||||
|
||||
export default interface ITreeViewElement {
|
||||
kind: String
|
||||
name: String
|
||||
shortName: String
|
||||
stableId: Number | String
|
||||
chips: Chip[]
|
||||
children: ITreeViewElement[]
|
||||
|
||||
// This is used for compatibility with the "legacy" Winscope infrastructure
|
||||
// where a class object would cause things to not work properly so this should
|
||||
// return a raw javascript object with the relevant information.
|
||||
// IMPORTANT: The implementation of this function should always return the
|
||||
// same object every time it is called and not generate a new object.
|
||||
asRawTreeViewObject(): TreeViewObject
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import Chip from './Chip'
|
||||
|
||||
export type TreeViewObject = {
|
||||
kind: String
|
||||
name: String
|
||||
shortName: String
|
||||
stableId: String | Number
|
||||
chips: Chip[]
|
||||
obj: any
|
||||
children: TreeViewObject[]
|
||||
ref: any
|
||||
}
|
||||
@@ -14,23 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { Activity } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
Activity.fromProto = function (proto): Activity {
|
||||
Activity.fromProto = function (proto: any): Activity {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const children = proto.windowToken.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ true))
|
||||
const windowContainer = WindowContainer.fromProto({proto: proto.windowToken.windowContainer,
|
||||
children: children, identifierOverride: proto.identifier})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.windowToken.windowContainer,
|
||||
/* protoChildren */ proto.windowToken.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ true,
|
||||
/* nameOverride */ null,
|
||||
/* identifierOverride */ proto.identifier
|
||||
);
|
||||
|
||||
const entry = new Activity(
|
||||
proto.name,
|
||||
proto.state,
|
||||
@@ -39,16 +38,18 @@ Activity.fromProto = function (proto): Activity {
|
||||
proto.procId,
|
||||
proto.translucent,
|
||||
windowContainer
|
||||
)
|
||||
);
|
||||
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
export default Activity
|
||||
function addAttributes(entry: Activity, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
export default Activity;
|
||||
|
||||
@@ -14,33 +14,33 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { DisplayArea } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
DisplayArea.fromProto = function (proto, isActivityInTree: Boolean): DisplayArea {
|
||||
DisplayArea.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayArea {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const children = proto.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
|
||||
children: children, nameOverride: proto.name})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer)
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.windowContainer,
|
||||
/* protoChildren */ proto.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree,
|
||||
/* nameOverride */ proto.name
|
||||
);
|
||||
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer);
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
export default DisplayArea
|
||||
function addAttributes(entry: DisplayArea, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
export default DisplayArea;
|
||||
|
||||
@@ -14,31 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { toRect, DisplayContent, Rect } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayContent {
|
||||
DisplayContent.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayContent {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const children = proto.rootDisplayArea.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
const windowContainer = WindowContainer.fromProto({proto: proto.rootDisplayArea.windowContainer,
|
||||
children: children, nameOverride: proto.displayInfo?.name ?? null})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
|
||||
const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0
|
||||
const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0
|
||||
const appRectWidth = proto.displayInfo?.appWidth ?? 0
|
||||
const appRectHeight = proto.displayInfo?.appHeight ?? 0
|
||||
|
||||
const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null
|
||||
const movementBounds = proto.pinnedStackController?.movementBounds ?? null
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.rootDisplayArea.windowContainer,
|
||||
/* protoChildren */ proto.rootDisplayArea.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree,
|
||||
/* nameOverride */ proto.displayInfo?.name ?? null
|
||||
);
|
||||
const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0;
|
||||
const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0;
|
||||
const appRectWidth = proto.displayInfo?.appWidth ?? 0;
|
||||
const appRectHeight = proto.displayInfo?.appHeight ?? 0;
|
||||
const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null;
|
||||
const movementBounds = proto.pinnedStackController?.movementBounds ?? null;
|
||||
|
||||
const entry = new DisplayContent(
|
||||
proto.id,
|
||||
@@ -59,16 +54,18 @@ DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayC
|
||||
proto.displayRotation?.rotation ?? 0,
|
||||
proto.displayRotation?.lastOrientation ?? 0,
|
||||
windowContainer
|
||||
)
|
||||
);
|
||||
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
export default DisplayContent
|
||||
function addAttributes(entry: DisplayContent, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
export default DisplayContent;
|
||||
|
||||
@@ -14,24 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { Task, toRect } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
Task.fromProto = function (proto, isActivityInTree: Boolean): Task {
|
||||
Task.fromProto = function (proto: any, isActivityInTree: Boolean): Task {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer
|
||||
const children = windowContainerProto.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
const windowContainer = WindowContainer.fromProto({proto: windowContainerProto,
|
||||
children: children})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer;
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ windowContainerProto,
|
||||
/* protoChildren */ windowContainerProto.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree
|
||||
);
|
||||
|
||||
const entry = new Task(
|
||||
proto.taskFragment?.activityType ?? proto.activityType,
|
||||
proto.fillsParent,
|
||||
@@ -51,16 +48,18 @@ Task.fromProto = function (proto, isActivityInTree: Boolean): Task {
|
||||
proto.taskFragment?.minWidth ?? proto.minWidth,
|
||||
proto.taskFragment?.minHeight ?? proto.minHeight,
|
||||
windowContainer
|
||||
)
|
||||
);
|
||||
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
export default Task
|
||||
function addAttributes(entry: Task, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
export default Task;
|
||||
|
||||
@@ -14,39 +14,36 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { TaskFragment } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
TaskFragment.fromProto = function (proto, isActivityInTree: Boolean): TaskFragment {
|
||||
TaskFragment.fromProto = function (proto: any, isActivityInTree: Boolean): TaskFragment {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const children = proto.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
|
||||
children: children})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.windowContainer,
|
||||
/* protoChildren */ proto.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree);
|
||||
const entry = new TaskFragment(
|
||||
proto.activityType,
|
||||
proto.displayId,
|
||||
proto.minWidth,
|
||||
proto.minHeight,
|
||||
windowContainer
|
||||
)
|
||||
);
|
||||
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
export default TaskFragment
|
||||
function addAttributes(entry: TaskFragment, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
export default TaskFragment;
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
|
||||
import {
|
||||
Configuration,
|
||||
@@ -34,21 +33,27 @@ import TaskFragment from "./TaskFragment"
|
||||
import WindowState from "./WindowState"
|
||||
import WindowToken from "./WindowToken"
|
||||
|
||||
WindowContainer.fromProto = function ({
|
||||
proto,
|
||||
children,
|
||||
nameOverride = null,
|
||||
identifierOverride = null,
|
||||
tokenOverride = null
|
||||
}): WindowContainer {
|
||||
WindowContainer.fromProto = function (
|
||||
proto: any,
|
||||
protoChildren: any[],
|
||||
isActivityInTree: boolean,
|
||||
nameOverride: string = null,
|
||||
identifierOverride: string = null,
|
||||
tokenOverride = null,
|
||||
): WindowContainer {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
const identifier = identifierOverride ?? proto.identifier
|
||||
var name = nameOverride ?? identifier?.title ?? ""
|
||||
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? ""
|
||||
|
||||
const config = newConfigurationContainer(proto.configurationContainer)
|
||||
const children = protoChildren
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree));
|
||||
|
||||
const identifier = identifierOverride ?? proto.identifier;
|
||||
var name = nameOverride ?? identifier?.title ?? "";
|
||||
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? "";
|
||||
|
||||
const config = createConfigurationContainer(proto.configurationContainer);
|
||||
const entry = new WindowContainer(
|
||||
name,
|
||||
token,
|
||||
@@ -56,19 +61,19 @@ WindowContainer.fromProto = function ({
|
||||
proto.visible,
|
||||
config,
|
||||
children
|
||||
)
|
||||
);
|
||||
|
||||
// we remove the children property from the object to avoid it showing the
|
||||
// the properties view of the element as we can always see those elements'
|
||||
// properties by changing the target element in the hierarchy tree view.
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
return entry;
|
||||
}
|
||||
|
||||
WindowContainer.childrenFromProto = function(proto, isActivityInTree: Boolean): WindowContainerChild {
|
||||
function addAttributes(entry: WindowContainer, proto: any) {
|
||||
entry.proto = proto;
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
}
|
||||
|
||||
WindowContainer.childrenFromProto = function(proto: any, isActivityInTree: Boolean): WindowContainerChild {
|
||||
return DisplayContent.fromProto(proto.displayContent, isActivityInTree) ??
|
||||
DisplayArea.fromProto(proto.displayArea, isActivityInTree) ??
|
||||
Task.fromProto(proto.task, isActivityInTree) ??
|
||||
@@ -76,28 +81,28 @@ WindowContainer.childrenFromProto = function(proto, isActivityInTree: Boolean):
|
||||
Activity.fromProto(proto.activity) ??
|
||||
WindowToken.fromProto(proto.windowToken, isActivityInTree) ??
|
||||
WindowState.fromProto(proto.window, isActivityInTree) ??
|
||||
WindowContainer.fromProto({proto: proto.windowContainer})
|
||||
WindowContainer.fromProto(proto.windowContainer);
|
||||
}
|
||||
|
||||
function newConfigurationContainer(proto): ConfigurationContainer {
|
||||
function createConfigurationContainer(proto: any): ConfigurationContainer {
|
||||
const entry = new ConfigurationContainer(
|
||||
newConfiguration(proto?.overrideConfiguration ?? null),
|
||||
newConfiguration(proto?.fullConfiguration ?? null),
|
||||
newConfiguration(proto?.mergedOverrideConfiguration ?? null)
|
||||
)
|
||||
createConfiguration(proto?.overrideConfiguration ?? null),
|
||||
createConfiguration(proto?.fullConfiguration ?? null),
|
||||
createConfiguration(proto?.mergedOverrideConfiguration ?? null)
|
||||
);
|
||||
|
||||
entry.obj = entry
|
||||
return entry
|
||||
entry.obj = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
function newConfiguration(proto): Configuration {
|
||||
function createConfiguration(proto: any): Configuration {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
var windowConfiguration = null
|
||||
var windowConfiguration = null;
|
||||
|
||||
if (proto != null && proto.windowConfiguration != null) {
|
||||
windowConfiguration = newWindowConfiguration(proto.windowConfiguration)
|
||||
windowConfiguration = createWindowConfiguration(proto.windowConfiguration);
|
||||
}
|
||||
|
||||
return new Configuration(
|
||||
@@ -109,17 +114,17 @@ function newConfiguration(proto): Configuration {
|
||||
proto?.smallestScreenWidthDp ?? 0,
|
||||
proto?.screenLayout ?? 0,
|
||||
proto?.uiMode ?? 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function newWindowConfiguration(proto): WindowConfiguration {
|
||||
function createWindowConfiguration(proto: any): WindowConfiguration {
|
||||
return new WindowConfiguration(
|
||||
toRect(proto.appBounds),
|
||||
toRect(proto.bounds),
|
||||
toRect(proto.maxBounds),
|
||||
proto.windowingMode,
|
||||
proto.activityType
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default WindowContainer
|
||||
export default WindowContainer;
|
||||
|
||||
@@ -14,55 +14,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { toRect, Bounds, WindowState, WindowLayoutParams } from "../common"
|
||||
import { shortenName } from '../mixin'
|
||||
import { toRect, Size, WindowState, WindowLayoutParams } from "../common"
|
||||
import { VISIBLE_CHIP } from '../treeview/Chips'
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
WindowState.fromProto = function (proto, isActivityInTree: Boolean): WindowState {
|
||||
WindowState.fromProto = function (proto: any, isActivityInTree: Boolean): WindowState {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
} else {
|
||||
const identifierName = proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? ""
|
||||
var windowType = 0
|
||||
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
|
||||
windowType = WindowState.WINDOW_TYPE_STARTING
|
||||
} else if (proto.animatingExit) {
|
||||
windowType = WindowState.WINDOW_TYPE_EXITING
|
||||
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
|
||||
windowType = WindowState.WINDOW_TYPE_STARTING
|
||||
}
|
||||
|
||||
var nameOverride = identifierName
|
||||
|
||||
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
|
||||
nameOverride = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length)
|
||||
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
|
||||
nameOverride = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length)
|
||||
}
|
||||
|
||||
const children = proto.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
|
||||
const windowContainer = WindowContainer.fromProto({
|
||||
proto: proto.windowContainer,
|
||||
children: children,
|
||||
nameOverride: nameOverride,
|
||||
identifierOverride: proto.identifier})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const windowParams = createWindowLayoutParams(proto.attributes);
|
||||
const identifierName = getIdentifier(proto);
|
||||
const windowType = getWindowType(proto, identifierName);
|
||||
const name = getName(identifierName);
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.windowContainer,
|
||||
/* protoChildren */ proto.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree,
|
||||
/* nameOverride */ name,
|
||||
/* identifierOverride */ proto.identifier
|
||||
);
|
||||
|
||||
const entry = new WindowState(
|
||||
newWindowLayoutParams(proto.attributes),
|
||||
windowParams,
|
||||
proto.displayId,
|
||||
proto.stackId,
|
||||
proto.animator?.surface?.layer ?? 0,
|
||||
proto.animator?.surface?.shown ?? false,
|
||||
windowType,
|
||||
new Bounds(proto.requestedWidth, proto.requestedHeight),
|
||||
new Size(proto.requestedWidth, proto.requestedHeight),
|
||||
toRect(proto.surfacePosition),
|
||||
toRect(proto.windowFrames?.frame ?? null),
|
||||
toRect(proto.windowFrames?.containingFrame ?? null),
|
||||
@@ -74,22 +54,14 @@ import WindowContainer from "./WindowContainer"
|
||||
toRect(proto.animator?.lastClipRect ?? null),
|
||||
windowContainer,
|
||||
/* isAppWindow */ isActivityInTree
|
||||
)
|
||||
);
|
||||
|
||||
entry.kind = entry.constructor.name
|
||||
entry.rect = entry.frame
|
||||
entry.rect.ref = entry
|
||||
entry.rect.label = entry.name
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.visible = entry.isVisible ?? false
|
||||
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : []
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
return entry
|
||||
addAttributes(entry, proto);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
function newWindowLayoutParams(proto): WindowLayoutParams {
|
||||
function createWindowLayoutParams(proto: any): WindowLayoutParams {
|
||||
return new WindowLayoutParams(
|
||||
/* type */ proto?.type ?? 0,
|
||||
/* x */ proto?.x ?? 0,
|
||||
@@ -124,4 +96,42 @@ function newWindowLayoutParams(proto): WindowLayoutParams {
|
||||
)
|
||||
}
|
||||
|
||||
function getWindowType(proto: any, identifierName: string): number {
|
||||
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
|
||||
return WindowState.WINDOW_TYPE_STARTING;
|
||||
} else if (proto.animatingExit) {
|
||||
return WindowState.WINDOW_TYPE_EXITING;
|
||||
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
|
||||
return WindowState.WINDOW_TYPE_STARTING;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getName(identifierName: string): string {
|
||||
var name = identifierName;
|
||||
|
||||
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
|
||||
name = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length);
|
||||
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
|
||||
name = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function getIdentifier(proto: any): string {
|
||||
return proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? "";
|
||||
}
|
||||
|
||||
function addAttributes(entry: WindowState, proto: any) {
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.rect = entry.frame;
|
||||
entry.rect.ref = entry;
|
||||
entry.rect.label = entry.name;
|
||||
entry.proto = proto;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : [];
|
||||
}
|
||||
|
||||
export default WindowState
|
||||
|
||||
@@ -14,32 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getPropertiesForDisplay, shortenName } from '../mixin'
|
||||
import { asRawTreeViewObject } from '../../utils/diff.js'
|
||||
import { shortenName } from '../mixin'
|
||||
import { WindowToken } from "../common"
|
||||
import WindowContainer from "./WindowContainer"
|
||||
|
||||
WindowToken.fromProto = function (proto, isActivityInTree: Boolean): WindowToken {
|
||||
WindowToken.fromProto = function (proto: any, isActivityInTree: Boolean): WindowToken {
|
||||
if (proto == null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const children = proto.windowContainer.children.reverse()
|
||||
.filter(it => it != null)
|
||||
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
|
||||
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
|
||||
children: children, tokenOverride: proto.hashCode})
|
||||
if (windowContainer == null) {
|
||||
throw "Window container should not be null: " + JSON.stringify(proto)
|
||||
}
|
||||
const entry = new WindowToken(windowContainer)
|
||||
entry.kind = entry.constructor.name
|
||||
entry.obj = getPropertiesForDisplay(proto, entry)
|
||||
entry.shortName = shortenName(entry.name)
|
||||
entry.rawTreeViewObject = asRawTreeViewObject(entry)
|
||||
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
|
||||
return entry
|
||||
const windowContainer = WindowContainer.fromProto(
|
||||
/* proto */ proto.windowContainer,
|
||||
/* protoChildren */ proto.windowContainer.children.reverse(),
|
||||
/* isActivityInTree */ isActivityInTree,
|
||||
/* nameOverride */ null,
|
||||
/* identifierOverride */ null,
|
||||
/* tokenOverride */ proto.hashCode
|
||||
);
|
||||
const entry = new WindowToken(windowContainer);
|
||||
entry.kind = entry.constructor.name;
|
||||
entry.proto = proto;
|
||||
entry.shortName = shortenName(entry.name);
|
||||
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
|
||||
return entry;
|
||||
}
|
||||
|
||||
export default WindowToken
|
||||
export default WindowToken;
|
||||
|
||||
@@ -252,6 +252,9 @@ const store = new Vuex.Store({
|
||||
},
|
||||
updateTimelineTime(context, timestamp) {
|
||||
for (const file of context.getters.files) {
|
||||
//dumps do not have a timeline, so only look at files with timelines to update the timestamp
|
||||
if (!file.timeline) continue;
|
||||
|
||||
const type = file.type;
|
||||
const entryIndex = findLastMatchingSorted(
|
||||
file.timeline,
|
||||
|
||||
@@ -14,198 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* transform type flags */
|
||||
const TRANSLATE_VAL = 0x0001;
|
||||
const ROTATE_VAL = 0x0002;
|
||||
const SCALE_VAL = 0x0004;
|
||||
|
||||
/* orientation flags */
|
||||
const FLIP_H_VAL = 0x0100; // (1 << 0 << 8)
|
||||
const FLIP_V_VAL = 0x0200; // (1 << 1 << 8)
|
||||
const ROT_90_VAL = 0x0400; // (1 << 2 << 8)
|
||||
const ROT_INVALID_VAL = 0x8000; // (0x80 << 8)
|
||||
|
||||
function is_proto_2(transform) {
|
||||
/*
|
||||
* Checks if the loaded file was a stored with ProtoBuf2 or Protobuf3
|
||||
*
|
||||
* Proto2 files don't have a Type for the transform object but all other
|
||||
* fields of the transform are set.
|
||||
*
|
||||
* Proto3 has a type field for the transform but doesn't store default
|
||||
* values (0 for transform type), also, the framework/native implementation
|
||||
* doesn't write a transform in case it is an identity matrix.
|
||||
*/
|
||||
var propertyNames = Object.getOwnPropertyNames(transform);
|
||||
return (!propertyNames.includes("type") && propertyNames.includes("dsdx"));
|
||||
}
|
||||
|
||||
function is_simple_transform(transform) {
|
||||
transform = transform || {};
|
||||
if (is_proto_2(transform)) {
|
||||
return false;
|
||||
}
|
||||
return is_type_flag_clear(transform, ROT_INVALID_VAL|SCALE_VAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a transform type into readable format.
|
||||
* Adapted from the dump function from framework/native
|
||||
*
|
||||
* @param {*} transform Transform object ot be converter
|
||||
*/
|
||||
function format_transform_type(transform) {
|
||||
if (is_proto_2(transform)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL | TRANSLATE_VAL)) {
|
||||
return "IDENTITY";
|
||||
}
|
||||
|
||||
var type_flags = [];
|
||||
if (is_type_flag_set(transform, SCALE_VAL)) {
|
||||
type_flags.push("SCALE");
|
||||
}
|
||||
if (is_type_flag_set(transform, TRANSLATE_VAL)) {
|
||||
type_flags.push("TRANSLATE");
|
||||
}
|
||||
|
||||
if (is_type_flag_set(transform, ROT_INVALID_VAL)) {
|
||||
type_flags.push("ROT_INVALID");
|
||||
} else if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
|
||||
type_flags.push("ROT_270");
|
||||
} else if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
|
||||
type_flags.push("ROT_180");
|
||||
} else {
|
||||
if (is_type_flag_set(transform, ROT_90_VAL)) {
|
||||
type_flags.push("ROT_90");
|
||||
}
|
||||
if (is_type_flag_set(transform, FLIP_V_VAL)) {
|
||||
type_flags.push("FLIP_V");
|
||||
}
|
||||
if (is_type_flag_set(transform, FLIP_H_VAL)) {
|
||||
type_flags.push("FLIP_H");
|
||||
}
|
||||
}
|
||||
|
||||
if (type_flags.length == 0) {
|
||||
throw "Unknown transform type " + transform ;
|
||||
}
|
||||
|
||||
return type_flags.join(', ');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensures all values of the transform object are set.
|
||||
*/
|
||||
function fill_transform_data(transform) {
|
||||
function fill_simple_transform(transform) {
|
||||
// ROT_270 = ROT_90|FLIP_H|FLIP_V;
|
||||
if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
|
||||
transform.dsdx = 0.0;
|
||||
transform.dtdx = -1.0;
|
||||
transform.dsdy = 1.0;
|
||||
transform.dtdy = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// ROT_180 = FLIP_H|FLIP_V;
|
||||
if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
|
||||
transform.dsdx = -1.0;
|
||||
transform.dtdx = 0.0;
|
||||
transform.dsdy = 0.0;
|
||||
transform.dtdy = -1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// ROT_90
|
||||
if (is_type_flag_set(transform, ROT_90_VAL)) {
|
||||
transform.dsdx = 0.0;
|
||||
transform.dtdx = 1.0;
|
||||
transform.dsdy = -1.0;
|
||||
transform.dtdy = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// IDENTITY
|
||||
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL)) {
|
||||
transform.dsdx = 1.0;
|
||||
transform.dtdx = 0.0;
|
||||
transform.dsdy = 0.0;
|
||||
transform.dtdy = 1.0;
|
||||
transform.type = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
throw "Unknown transform type " + transform;
|
||||
}
|
||||
|
||||
if (!transform) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_proto_2(transform)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_simple_transform(transform)){
|
||||
fill_simple_transform(transform);
|
||||
}
|
||||
|
||||
transform.dsdx = transform.dsdx || 0.0;
|
||||
transform.dtdx = transform.dtdx || 0.0;
|
||||
transform.dsdy = transform.dsdy || 0.0;
|
||||
transform.dtdy = transform.dtdy || 0.0;
|
||||
}
|
||||
|
||||
function is_type_flag_set(transform, bits) {
|
||||
transform = transform || {};
|
||||
var type = transform.type || 0;
|
||||
return (type & bits) === bits;
|
||||
}
|
||||
|
||||
function is_type_flag_clear(transform, bits) {
|
||||
transform = transform || {};
|
||||
var type = transform.type || 0;
|
||||
return (type & bits) === 0;
|
||||
}
|
||||
|
||||
function multiply_vec2(matrix, x, y) {
|
||||
function multiplyVec2(matrix, x, y) {
|
||||
if (!matrix) return {x, y};
|
||||
// |dsdx dsdy tx| | x |
|
||||
// |dtdx dtdy ty| x | y |
|
||||
// |0 0 1 | | 1 |
|
||||
return {
|
||||
x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
|
||||
y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty
|
||||
y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty,
|
||||
};
|
||||
}
|
||||
|
||||
function multiply_rect(matrix, rect) {
|
||||
function multiplyRect(transform, rect) {
|
||||
let matrix = transform;
|
||||
if (transform && transform.matrix) {
|
||||
matrix = transform.matrix;
|
||||
}
|
||||
// |dsdx dsdy tx| | left, top |
|
||||
// matrix = |dtdx dtdy ty| rect = | |
|
||||
// |0 0 1 | | right, bottom |
|
||||
|
||||
var left_top = multiply_vec2(matrix, rect.left, rect.top);
|
||||
var right_top = multiply_vec2(matrix, rect.right, rect.top);
|
||||
var left_bottom = multiply_vec2(matrix, rect.left, rect.bottom);
|
||||
var right_bottom = multiply_vec2(matrix, rect.right, rect.bottom);
|
||||
const leftTop = multiplyVec2(matrix, rect.left, rect.top);
|
||||
const rightTop = multiplyVec2(matrix, rect.right, rect.top);
|
||||
const leftBottom = multiplyVec2(matrix, rect.left, rect.bottom);
|
||||
const rightBottom = multiplyVec2(matrix, rect.right, rect.bottom);
|
||||
|
||||
var outrect = {};
|
||||
outrect.left = Math.min(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
|
||||
outrect.top = Math.min(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
|
||||
outrect.right = Math.max(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
|
||||
outrect.bottom = Math.max(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
|
||||
const outrect = {};
|
||||
outrect.left = Math.min(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
|
||||
outrect.top = Math.min(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y);
|
||||
outrect.right = Math.max(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
|
||||
outrect.bottom = Math.max(leftTop.y, rightTop.y, leftBottom.y,
|
||||
rightBottom.y);
|
||||
return outrect;
|
||||
}
|
||||
|
||||
// Returns true if the applying the transform on an an axis aligned rectangle
|
||||
// results in another axis aligned rectangle.
|
||||
function is_simple_rotation(transform) {
|
||||
return !is_type_flag_set(transform, ROT_INVALID_VAL);
|
||||
}
|
||||
|
||||
export {format_transform_type, fill_transform_data, is_simple_transform,
|
||||
multiply_rect, is_simple_rotation};
|
||||
export {multiplyRect};
|
||||
|
||||
@@ -19,6 +19,7 @@ import {TRACE_TYPES, DUMP_TYPES} from '@/decode.js';
|
||||
const mixin = {
|
||||
showInTraceView(file) {
|
||||
return file.type == TRACE_TYPES.WINDOW_MANAGER ||
|
||||
file.type == TRACE_TYPES.ACCESSIBILITY ||
|
||||
file.type == TRACE_TYPES.SURFACE_FLINGER ||
|
||||
file.type == TRACE_TYPES.WAYLAND ||
|
||||
file.type == TRACE_TYPES.SYSTEM_UI ||
|
||||
@@ -30,6 +31,9 @@ const mixin = {
|
||||
file.type == DUMP_TYPES.SURFACE_FLINGER ||
|
||||
file.type == DUMP_TYPES.WAYLAND;
|
||||
},
|
||||
showInAccessibilityTraceView(file) {
|
||||
return file.type == TRACE_TYPES.ACCESSIBILITY;
|
||||
},
|
||||
showInWindowManagerTraceView(file) {
|
||||
return file.type == TRACE_TYPES.WINDOW_MANAGER ||
|
||||
file.type == DUMP_TYPES.WINDOW_MANAGER;
|
||||
|
||||
@@ -229,7 +229,14 @@ export default {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
const timestamp = parseInt(this.timeline[clickedOnTsIndex]);
|
||||
|
||||
var timestamp = parseInt(this.timeline[clickedOnTsIndex]);
|
||||
|
||||
//pointWidth is always 1
|
||||
//if offset percentage < 1, clickedOnTsIndex becomes negative, leading to a negative index
|
||||
if (clickedOnTsIndex < 0) {
|
||||
timestamp = parseInt(this.timeline[0])
|
||||
}
|
||||
this.$store.dispatch('updateTimelineTime', timestamp);
|
||||
},
|
||||
|
||||
|
||||
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility class for deriving state and visibility from the hierarchy. This
|
||||
* duplicates some of the logic in surface flinger. If the trace contains
|
||||
* composition state (visibleRegion), it will be used otherwise it will be
|
||||
* derived.
|
||||
*/
|
||||
import {multiply_rect, is_simple_rotation} from './matrix_utils.js';
|
||||
|
||||
// Layer flags
|
||||
const FLAG_HIDDEN = 0x01;
|
||||
const FLAG_OPAQUE = 0x02;
|
||||
const FLAG_SECURE = 0x80;
|
||||
|
||||
function flags_to_string(flags) {
|
||||
if (!flags) return '';
|
||||
const verboseFlags = [];
|
||||
if (flags & FLAG_HIDDEN) verboseFlags.push('HIDDEN');
|
||||
if (flags & FLAG_OPAQUE) verboseFlags.push('OPAQUE');
|
||||
if (flags & FLAG_SECURE) verboseFlags.push('SECURE');
|
||||
return verboseFlags.join('|') + ' (' + flags + ')';
|
||||
}
|
||||
|
||||
function is_empty(region) {
|
||||
return region == undefined ||
|
||||
region.rect == undefined ||
|
||||
region.rect.length == 0 ||
|
||||
region.rect.every(function(r) {
|
||||
return is_empty_rect(r);
|
||||
} );
|
||||
}
|
||||
|
||||
function is_empty_rect(rect) {
|
||||
const right = rect.right || 0;
|
||||
const left = rect.left || 0;
|
||||
const top = rect.top || 0;
|
||||
const bottom = rect.bottom || 0;
|
||||
|
||||
return (right - left) <= 0 || (bottom - top) <= 0;
|
||||
}
|
||||
|
||||
function is_rect_empty_and_valid(rect) {
|
||||
return rect &&
|
||||
(rect.left - rect.right === 0 || rect.top - rect.bottom === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The transformation matrix is defined as the product of:
|
||||
* | cos(a) -sin(a) | \/ | X 0 |
|
||||
* | sin(a) cos(a) | /\ | 0 Y |
|
||||
*
|
||||
* where a is a rotation angle, and X and Y are scaling factors.
|
||||
* A transformation matrix is invalid when either X or Y is zero,
|
||||
* as a rotation matrix is valid for any angle. When either X or Y
|
||||
* is 0, then the scaling matrix is not invertible, which makes the
|
||||
* transformation matrix not invertible as well. A 2D matrix with
|
||||
* components | A B | is not invertible if and only if AD - BC = 0.
|
||||
* | C D |
|
||||
* This check is included above.
|
||||
*/
|
||||
function is_transform_invalid(transform) {
|
||||
return !transform || (transform.dsdx * transform.dtdy ===
|
||||
transform.dtdx * transform.dsdy); // determinant of transform
|
||||
}
|
||||
|
||||
function is_opaque(layer) {
|
||||
if (layer.color == undefined || layer.color.a == undefined || layer.color.a != 1) return false;
|
||||
return layer.isOpaque;
|
||||
}
|
||||
|
||||
function fills_color(layer) {
|
||||
return layer.color && layer.color.a > 0 &&
|
||||
layer.color.r >= 0 && layer.color.g >= 0 &&
|
||||
layer.color.b >= 0;
|
||||
}
|
||||
|
||||
function draws_shadows(layer) {
|
||||
return layer.shadowRadius && layer.shadowRadius > 0;
|
||||
}
|
||||
|
||||
function has_blur(layer) {
|
||||
return layer.backgroundBlurRadius && layer.backgroundBlurRadius > 0;
|
||||
}
|
||||
|
||||
function has_effects(layer) {
|
||||
// Support previous color layer
|
||||
if (layer.type === 'ColorLayer') return true;
|
||||
|
||||
// Support newer effect layer
|
||||
return layer.type === 'EffectLayer' &&
|
||||
(fills_color(layer) || draws_shadows(layer) || has_blur(layer));
|
||||
}
|
||||
|
||||
function is_hidden_by_policy(layer) {
|
||||
return layer.flags & FLAG_HIDDEN == FLAG_HIDDEN ||
|
||||
// offscreen layer root has a unique layer id
|
||||
layer.id == 0x7FFFFFFD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the layer is visible based on its visibleRegion if available
|
||||
* or its type, active buffer content, alpha and properties.
|
||||
*/
|
||||
function is_visible(layer, hiddenByPolicy, includesCompositionState) {
|
||||
if (includesCompositionState) {
|
||||
return !is_empty(layer.visibleRegion);
|
||||
}
|
||||
|
||||
if (hiddenByPolicy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!layer.activeBuffer && !has_effects(layer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!layer.color || !layer.color.a || layer.color.a == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (layer.occludedBy && layer.occludedBy.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!layer.bounds || is_empty_rect(layer.bounds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_visibility_reason(layer, includesCompositionState) {
|
||||
if (layer.type === 'ContainerLayer') {
|
||||
return 'ContainerLayer';
|
||||
}
|
||||
|
||||
if (is_hidden_by_policy(layer)) {
|
||||
return 'Flag is hidden';
|
||||
}
|
||||
|
||||
if (layer.hidden) {
|
||||
return 'Hidden by parent';
|
||||
}
|
||||
|
||||
const isBufferLayer = (layer.type === 'BufferStateLayer' ||
|
||||
layer.type === 'BufferQueueLayer');
|
||||
if (isBufferLayer && (!layer.activeBuffer ||
|
||||
layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
|
||||
return 'Buffer is empty';
|
||||
}
|
||||
|
||||
if (!layer.color || !layer.color.a || layer.color.a == 0) {
|
||||
return 'Alpha is 0';
|
||||
}
|
||||
|
||||
if (is_rect_empty_and_valid(layer.crop)) {
|
||||
return 'Crop is 0x0';
|
||||
}
|
||||
|
||||
if (!layer.bounds || is_empty_rect(layer.bounds)) {
|
||||
return 'Bounds is 0x0';
|
||||
}
|
||||
|
||||
if (is_transform_invalid(layer.transform)) {
|
||||
return 'Transform is invalid';
|
||||
}
|
||||
if (layer.isRelativeOf && layer.zOrderRelativeOf == -1) {
|
||||
return 'RelativeOf layer has been removed';
|
||||
}
|
||||
|
||||
const isEffectLayer = (layer.type === 'EffectLayer');
|
||||
if (isEffectLayer && !fills_color(layer) &&
|
||||
!draws_shadows(layer) && !has_blur(layer)) {
|
||||
return 'Effect layer does not have color fill, shadow or blur';
|
||||
}
|
||||
|
||||
if (layer.occludedBy && layer.occludedBy.length > 0) {
|
||||
return 'Layer is occluded by:' + layer.occludedBy.join();
|
||||
}
|
||||
|
||||
if (includesCompositionState && is_empty(layer.visibleRegion)) {
|
||||
return 'Visible region calculated by Composition Engine is empty';
|
||||
}
|
||||
|
||||
if (layer.visible) {
|
||||
return 'Unknown';
|
||||
};
|
||||
}
|
||||
|
||||
// Returns true if rectA overlaps rectB
|
||||
function overlaps(rectA, rectB) {
|
||||
return rectA.left < rectB.right && rectA.right > rectB.left &&
|
||||
rectA.top < rectB.bottom && rectA.bottom > rectA.top;
|
||||
}
|
||||
|
||||
// Returns true if outer rect contains inner rect
|
||||
function contains(outerLayer, innerLayer) {
|
||||
if (!is_simple_rotation(outerLayer.transform) ||
|
||||
!is_simple_rotation(innerLayer.transform)) {
|
||||
return false;
|
||||
}
|
||||
const outer = screen_bounds(outerLayer);
|
||||
const inner = screen_bounds(innerLayer);
|
||||
return inner.left >= outer.left && inner.top >= outer.top &&
|
||||
inner.right <= outer.right && inner.bottom <= outer.bottom;
|
||||
}
|
||||
|
||||
function screen_bounds(layer) {
|
||||
if (layer.screenBounds) return layer.screenBounds;
|
||||
const transformMatrix = layer.transform;
|
||||
const tx = layer.position ? layer.position.x || 0 : 0;
|
||||
const ty = layer.position ? layer.position.y || 0 : 0;
|
||||
|
||||
transformMatrix.tx = tx;
|
||||
transformMatrix.ty = ty;
|
||||
return multiply_rect(transformMatrix, layer.bounds);
|
||||
}
|
||||
|
||||
// Traverse in z-order from top to bottom and fill in occlusion data
|
||||
function fill_occlusion_state(layerMap, rootLayers, includesCompositionState) {
|
||||
const layers = rootLayers.filter((layer) => !layer.isRelativeOf);
|
||||
traverse_top_to_bottom(layerMap, layers, {opaqueRects: [], transparentRects: [], screenBounds: null}, (layer, globalState) => {
|
||||
if (layer.name.startsWith('Root#0') && layer.sourceBounds) {
|
||||
globalState.screenBounds = {left: 0, top: 0, bottom: layer.sourceBounds.bottom, right: layer.sourceBounds.right};
|
||||
}
|
||||
|
||||
const visible = is_visible(layer, layer.hidden, includesCompositionState);
|
||||
if (visible) {
|
||||
const fullyOccludes = (testLayer) => contains(testLayer, layer) && !layer.cornerRadius;
|
||||
const partiallyOccludes = (testLayer) => overlaps(testLayer, layer);
|
||||
const covers = (testLayer) => overlaps(testLayer, layer);
|
||||
|
||||
layer.occludedBy = globalState.opaqueRects.filter(fullyOccludes).map((layer) => layer.id);
|
||||
layer.partiallyOccludedBy = globalState.opaqueRects.filter(partiallyOccludes)
|
||||
.filter((p) => layer.occludedBy.indexOf(p.id) == -1)
|
||||
.map((layer) => layer.id);
|
||||
layer.coveredBy = globalState.transparentRects.filter(covers).map((layer) => layer.id);
|
||||
|
||||
if (is_opaque(layer)) {
|
||||
globalState.opaqueRects.push(layer);
|
||||
} else {
|
||||
globalState.transparentRects.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
layer.visible = is_visible(layer, layer.hidden, includesCompositionState);
|
||||
if (!layer.visible) {
|
||||
layer.invisibleDueTo = get_visibility_reason(layer, includesCompositionState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function traverse_top_to_bottom(layerMap, rootLayers, globalState, fn) {
|
||||
for (let i = rootLayers.length-1; i >=0; i--) {
|
||||
const relatives = [];
|
||||
for (const id of rootLayers[i].relatives) {
|
||||
if (!layerMap.hasOwnProperty(id)) {
|
||||
// TODO (b/162500053): so that this doesn't need to be checked here
|
||||
console.warn(
|
||||
`Relative layer with id ${id} not found in dumped layers... ` +
|
||||
`Skipping layer in traversal...`);
|
||||
} else {
|
||||
relatives.push(layerMap[id]);
|
||||
}
|
||||
}
|
||||
|
||||
const children = [];
|
||||
for (const id of rootLayers[i].children) {
|
||||
if (!layerMap.hasOwnProperty(id)) {
|
||||
// TODO (b/162500053): so that this doesn't need to be checked here
|
||||
console.warn(
|
||||
`Child layer with id ${id} not found in dumped layers... ` +
|
||||
`Skipping layer in traversal...`);
|
||||
} else {
|
||||
children.push(layerMap[id]);
|
||||
}
|
||||
}
|
||||
|
||||
// traverse through relatives and children that are not relatives
|
||||
const traverseList = relatives
|
||||
.concat(children.filter((layer) => !layer.isRelativeOf));
|
||||
|
||||
traverseList.sort((lhs, rhs) => rhs.z - lhs.z);
|
||||
|
||||
traverseList.filter((layer) => layer.z >=0).forEach((layer) => {
|
||||
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
|
||||
});
|
||||
|
||||
fn(rootLayers[i], globalState);
|
||||
|
||||
traverseList.filter((layer) => layer.z < 0).forEach((layer) => {
|
||||
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse all children and fill in any inherited states.
|
||||
function fill_inherited_state(layerMap, rootLayers) {
|
||||
traverse(layerMap, rootLayers, (layer, parent) => {
|
||||
const parentHidden = parent && parent.hidden;
|
||||
layer.hidden = is_hidden_by_policy(layer) || parentHidden;
|
||||
layer.verboseFlags = flags_to_string(layer.flags);
|
||||
|
||||
if (!layer.bounds) {
|
||||
if (!layer.sourceBounds) {
|
||||
layer.bounds = layer.sourceBounds;
|
||||
} else if (parent) {
|
||||
layer.bounds = parent.bounds;
|
||||
} else {
|
||||
layer.bounds = {left: 0, top: 0, right: 0, bottom: 0};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function traverse(layerMap, rootLayers, fn) {
|
||||
for (let i = rootLayers.length-1; i >=0; i--) {
|
||||
const parentId = rootLayers[i].parent;
|
||||
const parent = parentId == -1 ? null : layerMap[parentId];
|
||||
fn(rootLayers[i], parent);
|
||||
const children = rootLayers[i].children.map(
|
||||
(id) => {
|
||||
const child = layerMap[id];
|
||||
if (child == null) {
|
||||
console.warn(
|
||||
`Child layer with id ${id} in parent layer id ${rootLayers[i].id} not found... ` +
|
||||
`Skipping layer in traversal...`);
|
||||
}
|
||||
return child;
|
||||
}).filter(item => item !== undefined);
|
||||
traverse(layerMap, children, fn);
|
||||
}
|
||||
}
|
||||
|
||||
export {fill_occlusion_state, fill_inherited_state};
|
||||
@@ -14,8 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ITreeViewElement from "./ITreeViewElement"
|
||||
import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
|
||||
import TraceBase from './TraceBase';
|
||||
|
||||
export default interface IClickableTreeViewElement extends ITreeViewElement {
|
||||
obj: any
|
||||
export default class Accessibility extends TraceBase {
|
||||
accessibilityTraceFile: Object;
|
||||
|
||||
constructor(files) {
|
||||
const accessibilityTraceFile = files[FILE_TYPES.ACCESSIBILITY_TRACE];
|
||||
super(accessibilityTraceFile.data, accessibilityTraceFile.timeline, files);
|
||||
|
||||
this.accessibilityTraceFile = accessibilityTraceFile;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return TRACE_TYPES.ACCESSIBILITY;
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
|
||||
import TraceBase from './TraceBase';
|
||||
import { LayersTrace } from '@/flickerlib';
|
||||
|
||||
export default class SurfaceFlinger extends TraceBase {
|
||||
sfTraceFile: any;
|
||||
sfTraceFile: Object;
|
||||
|
||||
constructor(files) {
|
||||
const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE];
|
||||
@@ -30,4 +31,8 @@ export default class SurfaceFlinger extends TraceBase {
|
||||
get type() {
|
||||
return TRACE_TYPES.SURFACE_FLINGER;
|
||||
}
|
||||
|
||||
static fromProto(proto: any): LayersTrace {
|
||||
return LayersTrace.fromProto(proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class WindowManager extends TraceBase {
|
||||
return TRACE_TYPES.WINDOW_MANAGER;
|
||||
}
|
||||
|
||||
static fromProto(proto): WindowManagerTrace {
|
||||
static fromProto(proto: any): WindowManagerTrace {
|
||||
return WindowManagerTrace.fromProto(proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ function transform({
|
||||
stableId: stableIdResolved,
|
||||
visible: call(visible, obj),
|
||||
childrenVisible: transformedChildren.some((c) => {
|
||||
return c.childrenVisible || c.visible;
|
||||
return c.childrenVisible || c.isVisible;
|
||||
}),
|
||||
flattened: call(flattened, obj),
|
||||
};
|
||||
@@ -309,7 +309,7 @@ class ObjectTransformer {
|
||||
|
||||
transformedObj = {
|
||||
kind: '',
|
||||
name: name + ': ' + child.name,
|
||||
name: (isTerminal(name) ? compareWithName : name) + ': ' + child.name,
|
||||
stableId,
|
||||
children: child.children,
|
||||
combined: true,
|
||||
@@ -376,6 +376,24 @@ function nanos_to_string(elapsedRealtimeNanos) {
|
||||
return parts.reverse().join('');
|
||||
}
|
||||
|
||||
function string_to_nanos(stringTime) {
|
||||
//isolate the times for each unit in an array
|
||||
var times = stringTime.split(/\D+/).filter(unit => unit.length > 0);
|
||||
|
||||
//add zeroes to start of array if only partial timestamp is input
|
||||
while (times.length<5) {
|
||||
times.unshift("0");
|
||||
}
|
||||
|
||||
var units = [24*60*60, 60*60, 60, 1, 0.001];
|
||||
var nanos = 0;
|
||||
//multiply the times by the relevant unit and sum
|
||||
for (var x=0; x<5; x++) {
|
||||
nanos += units[x]*parseInt(times[x]);
|
||||
}
|
||||
return nanos*(10**9);
|
||||
}
|
||||
|
||||
// Returns a UI element used highlight a visible entry.
|
||||
// eslint-disable-next-line camelcase
|
||||
function get_visible_chip() {
|
||||
@@ -383,4 +401,4 @@ function get_visible_chip() {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export {transform, ObjectTransformer, nanos_to_string, get_visible_chip};
|
||||
export {transform, ObjectTransformer, nanos_to_string, string_to_nanos, get_visible_chip};
|
||||
|
||||
52
tools/winscope/src/transform_accessibility.js
Normal file
52
tools/winscope/src/transform_accessibility.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2020, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { transform, nanos_to_string, get_visible_chip } from './transform.js'
|
||||
|
||||
function transform_accessibility(accessibility) {
|
||||
return transform({
|
||||
obj: accessibility,
|
||||
kind: 'accessibility',
|
||||
name: 'accessibility',
|
||||
children: []
|
||||
});
|
||||
}
|
||||
|
||||
function transform_entry(entry) {
|
||||
return transform({
|
||||
obj: entry,
|
||||
kind: 'entry',
|
||||
name: nanos_to_string(entry.elapsedRealtimeNanos),
|
||||
children: [
|
||||
[entry.accessibilityService, transform_accessibility],
|
||||
],
|
||||
timestamp: entry.elapsedRealtimeNanos,
|
||||
stableId: 'entry'
|
||||
});
|
||||
}
|
||||
|
||||
function transform_accessibility_trace(entries) {
|
||||
return transform({
|
||||
obj: entries,
|
||||
kind: 'entries',
|
||||
name: 'entries',
|
||||
children: [
|
||||
[entries.entry, transform_entry],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export { transform_accessibility_trace };
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017, The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import {transform, nanos_to_string, get_visible_chip} from './transform.js';
|
||||
// eslint-disable-next-line camelcase
|
||||
import {fill_occlusion_state, fill_inherited_state} from './sf_visibility.js';
|
||||
import {getSimplifiedLayerName} from './utils/names';
|
||||
import ObjectFormatter from './flickerlib/ObjectFormatter'
|
||||
|
||||
const RELATIVE_Z_CHIP = {
|
||||
short: 'RelZ',
|
||||
long: 'Is relative Z-ordered to another surface',
|
||||
class: 'warn',
|
||||
};
|
||||
const RELATIVE_Z_PARENT_CHIP = {
|
||||
short: 'RelZParent',
|
||||
long: 'Something is relative Z-ordered to this surface',
|
||||
class: 'warn',
|
||||
};
|
||||
const MISSING_LAYER = {
|
||||
short: 'MissingLayer',
|
||||
long:
|
||||
'This layer was referenced from the parent, but not present in the trace',
|
||||
class: 'error',
|
||||
};
|
||||
const GPU_CHIP = {
|
||||
short: 'GPU',
|
||||
long: 'This layer was composed on the GPU',
|
||||
class: 'gpu',
|
||||
};
|
||||
const HWC_CHIP = {
|
||||
short: 'HWC',
|
||||
long: 'This layer was composed by Hardware Composer',
|
||||
class: 'hwc',
|
||||
};
|
||||
|
||||
function transformLayer(layer) {
|
||||
function offsetTo(bounds, x, y) {
|
||||
return {
|
||||
right: bounds.right - (bounds.left - x),
|
||||
bottom: bounds.bottom - (bounds.top - y),
|
||||
left: x,
|
||||
top: y,
|
||||
};
|
||||
}
|
||||
|
||||
function getRect(layer) {
|
||||
let result = layer.bounds;
|
||||
const tx = layer.position ? layer.position.x || 0 : 0;
|
||||
const ty = layer.position ? layer.position.y || 0 : 0;
|
||||
result.label = layer.name;
|
||||
result.transform = layer.transform;
|
||||
result.transform.tx = tx;
|
||||
result.transform.ty = ty;
|
||||
return result;
|
||||
}
|
||||
|
||||
function addHwcCompositionTypeChip(layer) {
|
||||
if (layer.hwcCompositionType === 'CLIENT') {
|
||||
chips.push(GPU_CHIP);
|
||||
} else if (layer.hwcCompositionType === 'DEVICE' ||
|
||||
layer.hwcCompositionType === 'SOLID_COLOR') {
|
||||
chips.push(HWC_CHIP);
|
||||
}
|
||||
}
|
||||
|
||||
const chips = [];
|
||||
if (layer.visible) {
|
||||
chips.push(get_visible_chip());
|
||||
}
|
||||
if ((layer.zOrderRelativeOf || -1) !== -1) {
|
||||
chips.push(RELATIVE_Z_CHIP);
|
||||
}
|
||||
if (layer.zOrderRelativeParentOf !== undefined) {
|
||||
chips.push(RELATIVE_Z_PARENT_CHIP);
|
||||
}
|
||||
if (layer.missing) {
|
||||
chips.push(MISSING_LAYER);
|
||||
}
|
||||
addHwcCompositionTypeChip(layer);
|
||||
|
||||
const rect = layer.visible && layer.bounds !== null ?
|
||||
getRect(layer) : undefined;
|
||||
|
||||
const simplifiedLayerName = getSimplifiedLayerName(layer.name);
|
||||
const shortName = simplifiedLayerName ?
|
||||
layer.id + ': ' + simplifiedLayerName : undefined;
|
||||
|
||||
const transformedLayer = transform({
|
||||
obj: ObjectFormatter.format(layer),
|
||||
kind: '',
|
||||
name: layer.id + ': ' + layer.name,
|
||||
shortName,
|
||||
children: [[layer.resolvedChildren, transformLayer]],
|
||||
rect,
|
||||
undefined /* bounds */,
|
||||
highlight: rect,
|
||||
chips,
|
||||
visible: layer.visible,
|
||||
freeze: false,
|
||||
});
|
||||
|
||||
// NOTE: Temporary until refactored to use flickerlib
|
||||
transformedLayer.invisibleDueTo = layer.invisibleDueTo;
|
||||
transformedLayer.occludedBy = layer.occludedBy;
|
||||
transformedLayer.partiallyOccludedBy = layer.partiallyOccludedBy;
|
||||
transformedLayer.coveredBy = layer.coveredBy;
|
||||
|
||||
return Object.freeze(transformedLayer);
|
||||
}
|
||||
|
||||
function missingLayer(childId) {
|
||||
return {
|
||||
name: 'layer #' + childId,
|
||||
missing: true,
|
||||
zOrderRelativeOf: -1,
|
||||
transform: {dsdx: 1, dtdx: 0, dsdy: 0, dtdy: 1},
|
||||
};
|
||||
}
|
||||
|
||||
function transformLayers(includesCompositionState, layers) {
|
||||
const idToItem = {};
|
||||
const isChild = {};
|
||||
|
||||
const layersList = layers.layers || [];
|
||||
|
||||
layersList.forEach((e) => {
|
||||
idToItem[e.id] = e;
|
||||
});
|
||||
layersList.forEach((e) => {
|
||||
e.resolvedChildren = [];
|
||||
if (Array.isArray(e.children)) {
|
||||
e.resolvedChildren = e.children.map(
|
||||
(childId) => idToItem[childId] || missingLayer(childId));
|
||||
e.children.forEach((childId) => {
|
||||
isChild[childId] = true;
|
||||
});
|
||||
}
|
||||
// We don't clean up relatives when the relative parent is removed, so it
|
||||
// may be inconsistent
|
||||
if ((e.zOrderRelativeOf || -1) !== -1 && (idToItem[e.zOrderRelativeOf])) {
|
||||
idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
|
||||
}
|
||||
});
|
||||
|
||||
const roots = layersList.filter((e) => !isChild[e.id]);
|
||||
fill_inherited_state(idToItem, roots);
|
||||
|
||||
// Backwards compatibility check
|
||||
const occlusionDetectionCompatible = roots[0].bounds !== null;
|
||||
if (occlusionDetectionCompatible) {
|
||||
fill_occlusion_state(idToItem, roots, includesCompositionState);
|
||||
}
|
||||
function foreachTree(nodes, fun) {
|
||||
nodes.forEach((n) => {
|
||||
fun(n);
|
||||
foreachTree(n.children, fun);
|
||||
});
|
||||
}
|
||||
|
||||
const idToTransformed = {};
|
||||
const transformedRoots = roots.map((r) =>
|
||||
transformLayer(r, {
|
||||
parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
|
||||
parentHidden: null,
|
||||
}));
|
||||
|
||||
foreachTree(transformedRoots, (n) => {
|
||||
idToTransformed[n.obj.id] = n;
|
||||
});
|
||||
const flattened = [];
|
||||
layersList.forEach((e) => {
|
||||
flattened.push(idToTransformed[e.id]);
|
||||
});
|
||||
|
||||
return transform({
|
||||
obj: {},
|
||||
kind: 'layers',
|
||||
name: 'layers',
|
||||
children: [
|
||||
[transformedRoots, (c) => c],
|
||||
],
|
||||
rectsTransform(r) {
|
||||
const res = [];
|
||||
flattened.forEach((l) => {
|
||||
if (l.rect) {
|
||||
res.push(l.rect);
|
||||
}
|
||||
});
|
||||
return res.reverse();
|
||||
},
|
||||
flattened,
|
||||
});
|
||||
}
|
||||
|
||||
function transformLayersEntry(entry) {
|
||||
const includesCompositionState = !entry.excludesCompositionState;
|
||||
return transform({
|
||||
obj: entry,
|
||||
kind: 'entry',
|
||||
name: nanos_to_string(entry.elapsedRealtimeNanos) + ' - ' + entry.where,
|
||||
children: [
|
||||
[
|
||||
[entry.layers],
|
||||
(layer) => transformLayers(includesCompositionState, layer),
|
||||
],
|
||||
],
|
||||
timestamp: entry.elapsedRealtimeNanos,
|
||||
stableId: 'entry',
|
||||
});
|
||||
}
|
||||
|
||||
function transformLayersTrace(entries) {
|
||||
const r = transform({
|
||||
obj: entries,
|
||||
kind: 'layerstrace',
|
||||
name: 'layerstrace',
|
||||
children: [
|
||||
[entries.entry, transformLayersEntry],
|
||||
],
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
export {transformLayers, transformLayersTrace};
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
// TODO (b/162300507): Get rid of cloning
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import ObjectFormatter from '../flickerlib/ObjectFormatter';
|
||||
|
||||
export const DiffType = Object.freeze({
|
||||
NONE: 'none',
|
||||
@@ -26,21 +26,6 @@ export const DiffType = Object.freeze({
|
||||
MODIFIED: 'modified',
|
||||
});
|
||||
|
||||
export function asRawTreeViewObject(obj) {
|
||||
const children = obj.children?.map(child => child.rawTreeViewObject) ?? []
|
||||
|
||||
return {
|
||||
kind: obj.kind,
|
||||
name: obj.name,
|
||||
shortName: obj.shortName,
|
||||
stableId: obj.stableId,
|
||||
chips: obj.chips,
|
||||
obj: obj.obj,
|
||||
children,
|
||||
ref: obj,
|
||||
};
|
||||
}
|
||||
|
||||
export function defaultModifiedCheck(newNode, oldNode) {
|
||||
if (!newNode && !oldNode) {
|
||||
return false;
|
||||
@@ -50,29 +35,16 @@ export function defaultModifiedCheck(newNode, oldNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return JSON.stringify(newNode.obj) !== JSON.stringify(oldNode.obj);
|
||||
}
|
||||
|
||||
function isPrimitive(test) {
|
||||
return test !== Object(test);
|
||||
return !newNode.equals(oldNode);
|
||||
}
|
||||
|
||||
export class DiffGenerator {
|
||||
constructor(tree) {
|
||||
if (tree.rawTreeViewObject) {
|
||||
this.tree = tree.rawTreeViewObject;
|
||||
} else {
|
||||
this.tree = tree;
|
||||
}
|
||||
}
|
||||
|
||||
compareWith(tree) {
|
||||
if (tree?.rawTreeViewObject) {
|
||||
this.diffWithTree = tree.rawTreeViewObject;
|
||||
} else {
|
||||
this.diffWithTree = tree;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -140,16 +112,13 @@ export class DiffGenerator {
|
||||
return Object.keys(obj).length === 0 && obj.constructor === Object;
|
||||
}
|
||||
|
||||
_cloneNodeWithoutChildren(node) {
|
||||
const clone = {};
|
||||
|
||||
for (const key in node) {
|
||||
if (key !== 'children') {
|
||||
const val = node[key];
|
||||
clone[key] = isPrimitive(val) ? val : cloneDeep(val);
|
||||
}
|
||||
}
|
||||
|
||||
_cloneNode(node) {
|
||||
const clone = ObjectFormatter.cloneObject(node);
|
||||
clone.children = node.children;
|
||||
clone.name = node.name;
|
||||
clone.kind = node.kind;
|
||||
clone.stableId = node.stableId;
|
||||
clone.shortName = node.shortName;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -167,7 +136,7 @@ export class DiffGenerator {
|
||||
// Deep clone newTree omitting children field
|
||||
// Clone is required because trees are frozen objects — we can't
|
||||
// modify the original tree object. Also means there is no side effect.
|
||||
const diffTree = this._cloneNodeWithoutChildren(newTree);
|
||||
const diffTree = this._cloneNode(newTree);
|
||||
|
||||
// Default to no changes
|
||||
diffTree.diff = {type: DiffType.NONE};
|
||||
@@ -200,7 +169,7 @@ export class DiffGenerator {
|
||||
|
||||
// Check if oldTree has been deleted of moved
|
||||
if (oldTree && !newTreeSiblingIds.includes(oldId)) {
|
||||
const deletedTreeDiff = this._cloneNodeWithoutChildren(oldTree);
|
||||
const deletedTreeDiff = this._cloneNode(oldTree);
|
||||
|
||||
if (this.newMapping[oldId]) {
|
||||
deletedTreeDiff.diff = {type: DiffType.DELETED_MOVE};
|
||||
@@ -233,7 +202,7 @@ export class DiffGenerator {
|
||||
} else if (oldTree) {
|
||||
if (!newTreeSiblingIds.includes(oldId)) {
|
||||
// Deep clone oldTree omitting children field
|
||||
const diffTree = this._cloneNodeWithoutChildren(oldTree);
|
||||
const diffTree = this._cloneNode(oldTree);
|
||||
|
||||
// newTree doesn't exists, oldTree has either been moved or deleted.
|
||||
if (this.newMapping[oldId]) {
|
||||
@@ -258,12 +227,12 @@ export class DiffGenerator {
|
||||
|
||||
// TODO: Try replacing this with some sort of zipWith.
|
||||
const numOfChildren = Math.max(
|
||||
newTree?.children.length ?? 0, oldTree?.children.length ?? 0);
|
||||
newTree?.children?.length ?? 0, oldTree?.children?.length ?? 0);
|
||||
for (let i = 0; i < numOfChildren; i++) {
|
||||
const newChild = i < newTree?.children.length ?
|
||||
const newChild = i < newTree?.children?.length ?
|
||||
newTree.children[i] : null;
|
||||
|
||||
const oldChild = i < oldTree?.children.length ?
|
||||
const oldChild = i < oldTree?.children?.length ?
|
||||
oldTree.children[i] : null;
|
||||
|
||||
const childDiffTrees = this._generateDiffTree(
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||
const { VueLoaderPlugin } = require("vue-loader")
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const KotlinWebpackPlugin = require('@jetbrains/kotlin-webpack-plugin');
|
||||
const HtmlWebpackInlineSourcePlugin =
|
||||
@@ -129,7 +129,7 @@ const webpackConfig = {
|
||||
inlineSource: isDev ? false : '.(js|css)',
|
||||
template: 'src/index_template.html',
|
||||
}),
|
||||
new HtmlWebpackInlineSourcePlugin(),
|
||||
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
|
||||
new KotlinWebpackPlugin({
|
||||
src: [
|
||||
path.join(__dirname, '../../../platform_testing/libraries/flicker/' +
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user