Merge changes from topic "winscope-ui-tweak"
* changes: Added a Favicon for better tab identification Make the timeline size relative to the parent Cleanup the UI for better visibility Move the Video to the top next to the timeline Migrate to new MaterialVue
This commit is contained in:
1
tools/winscope/.gitignore
vendored
1
tools/winscope/.gitignore
vendored
@@ -2,3 +2,4 @@ node_modules/
|
||||
adb_proxy/venv/
|
||||
.vscode/
|
||||
dist/
|
||||
yarn-error.log
|
||||
@@ -10,13 +10,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^2.3.3",
|
||||
"vue-material": "0.8.1"
|
||||
"vue-material": "^1.0.0-beta-11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-preset-env": "^1.5.1",
|
||||
"cross-env": "^3.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^0.25.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"html-webpack-inline-source-plugin": "^0.0.9",
|
||||
|
||||
@@ -14,40 +14,73 @@
|
||||
-->
|
||||
<template>
|
||||
<div id="app">
|
||||
<md-whiteframe md-tag="md-toolbar">
|
||||
<md-app>
|
||||
<md-app-toolbar md-tag="md-toolbar">
|
||||
<h1 class="md-title" style="flex: 1">{{title}}</h1>
|
||||
<a class="md-button md-accent md-raised md-theme-default" @click="clear()" v-if="dataLoaded">Clear</a>
|
||||
</md-whiteframe>
|
||||
<div class="main-content">
|
||||
<md-layout v-if="!dataLoaded" class="m-2">
|
||||
<dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
|
||||
<datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
|
||||
</md-layout>
|
||||
<md-card v-if="dataLoaded">
|
||||
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<h2 class="md-title">Timeline</h2>
|
||||
<md-button
|
||||
class="md-accent md-raised md-theme-default"
|
||||
@click="clear()"
|
||||
v-if="dataLoaded"
|
||||
>Clear</md-button>
|
||||
</md-app-toolbar>
|
||||
<md-app-content class="main-content">
|
||||
<div class="md-layout m-2" v-if="!dataLoaded">
|
||||
<dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
|
||||
<datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
|
||||
</div>
|
||||
<div class="md-layout md-alignment-left-center " v-if="dataLoaded">
|
||||
<div class="md-layout-item video" v-if="video">
|
||||
<videoview :file="video" :ref="video.filename" />
|
||||
</div>
|
||||
<div class="md-layout-item">
|
||||
<md-toolbar
|
||||
md-elevation="0"
|
||||
class="md-transparent">
|
||||
<h2 class="md-title">Timeline </h2>
|
||||
<span> {{seekTime}} </span>
|
||||
<datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" />
|
||||
</md-whiteframe>
|
||||
</md-toolbar>
|
||||
<md-list>
|
||||
<md-list-item v-for="(file, idx) in files" :key="file.filename">
|
||||
<md-icon>{{file.type.icon}}</md-icon>
|
||||
<timeline :items="file.timeline" :selected-index="file.selectedIndex" :scale="scale" @item-selected="onTimelineItemSelected($event, idx)" class="timeline" />
|
||||
<md-icon>
|
||||
{{file.type.icon}}
|
||||
<md-tooltip md-direction="right">{{file.type.name}}</md-tooltip>
|
||||
</md-icon>
|
||||
<timeline
|
||||
:items="file.timeline"
|
||||
:selected-index="file.selectedIndex"
|
||||
:scale="scale"
|
||||
@item-selected="onTimelineItemSelected($event, idx)"
|
||||
class="timeline"
|
||||
/>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card>
|
||||
<dataview v-for="file in files" :key="file.filename" :ref="file.filename" :store="store" :file="file" @focus="onDataViewFocus(file.filename)" />
|
||||
</div>
|
||||
</div>
|
||||
<dataview
|
||||
v-for="file in files"
|
||||
:key="file.filename"
|
||||
:ref="file.filename"
|
||||
:store="store"
|
||||
:file="file"
|
||||
@focus="onDataViewFocus(file.filename)"
|
||||
/>
|
||||
</md-app-content>
|
||||
</md-app>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import TreeView from './TreeView.vue'
|
||||
import Timeline from './Timeline.vue'
|
||||
import Rects from './Rects.vue'
|
||||
import DataView from './DataView.vue'
|
||||
import VideoView from './VideoView.vue'
|
||||
import DataInput from './DataInput.vue'
|
||||
import LocalStore from './localstore.js'
|
||||
import DataAdb from './DataAdb.vue'
|
||||
import DataFilter from './DataFilter.vue'
|
||||
import FileType from './FileType.js'
|
||||
import { nanos_to_string } from './transform.js'
|
||||
|
||||
const APP_NAME = "Winscope"
|
||||
|
||||
@@ -68,9 +101,11 @@ function findLastMatchingSorted(array, predicate) {
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
mixins: [FileType],
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
video: null,
|
||||
title: APP_NAME,
|
||||
currentTimestamp: 0,
|
||||
activeDataView: null,
|
||||
@@ -143,7 +178,13 @@ export default {
|
||||
return true;
|
||||
},
|
||||
onDataReady(files) {
|
||||
this.files = files;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
if (this.isVideo(file)) {
|
||||
this.video = file;
|
||||
}
|
||||
this.files.push(file);
|
||||
}
|
||||
},
|
||||
setStatus(status) {
|
||||
if (status) {
|
||||
@@ -151,7 +192,7 @@ export default {
|
||||
} else {
|
||||
this.title = APP_NAME;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
prettyDump: function() { return JSON.stringify(this.dump, null, 2); },
|
||||
@@ -166,6 +207,9 @@ export default {
|
||||
this.activeDataView = this.files[0].filename;
|
||||
}
|
||||
return this.activeDataView;
|
||||
},
|
||||
seekTime: function() {
|
||||
return nanos_to_string(this.currentTimestamp);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -174,14 +218,14 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'timeline': Timeline,
|
||||
'dataview': DataView,
|
||||
'datainput': DataInput,
|
||||
'dataadb': DataAdb,
|
||||
'datafilter': DataFilter,
|
||||
},
|
||||
}
|
||||
|
||||
timeline: Timeline,
|
||||
dataview: DataView,
|
||||
videoview: VideoView,
|
||||
datainput: DataInput,
|
||||
dataadb: DataAdb,
|
||||
datafilter: DataFilter
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.main-content>* {
|
||||
@@ -201,14 +245,14 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.md-layout > .md-card {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.md-button {
|
||||
margin-top: 1em
|
||||
}
|
||||
|
||||
.video {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
@@ -227,5 +271,4 @@ li {
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="md-title">ADB Connect</div>
|
||||
</md-card-header>
|
||||
<md-card-content v-if="status === STATES.CONNECTING">
|
||||
<md-spinner md-indeterminate></md-spinner>
|
||||
<md-progress-spinner md-indeterminate></md-progress-spinner>
|
||||
</md-card-content>
|
||||
<md-card-content v-if="status === STATES.NO_PROXY">
|
||||
<md-icon class="md-accent">error</md-icon>
|
||||
@@ -52,10 +52,10 @@
|
||||
<md-card-content v-if="status === STATES.UNAUTH">
|
||||
<md-icon class="md-accent">lock</md-icon>
|
||||
<span class="md-subheading">Proxy authorisation required</span>
|
||||
<md-input-container>
|
||||
<md-field>
|
||||
<label>Enter Winscope proxy token</label>
|
||||
<md-input v-model="adbStore.proxyKey"></md-input>
|
||||
</md-input-container>
|
||||
</md-field>
|
||||
<div class="md-body-2">The proxy token is printed to console on proxy launch, copy and paste it above.</div>
|
||||
<div class="md-layout md-gutter">
|
||||
<md-button class="md-accent md-raised" @click="restart">Connect</md-button>
|
||||
@@ -65,15 +65,17 @@
|
||||
<div class="md-subheading">{{ Object.keys(devices).length > 0 ? "Connected devices:" : "No devices detected" }}</div>
|
||||
<md-list>
|
||||
<md-list-item v-for="(device, id) in devices" :key="id" @click="selectDevice(id)" :disabled="!device.authorised">
|
||||
<md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon><span>{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
|
||||
<md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon>
|
||||
<span class="md-list-item-text">{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<md-spinner :md-size="30" md-indeterminate></md-spinner>
|
||||
<md-progress-spinner :md-size="30" md-indeterminate></md-progress-spinner>
|
||||
</md-card-content>
|
||||
<md-card-content v-if="status === STATES.START_TRACE">
|
||||
<md-list>
|
||||
<md-list-item>
|
||||
<md-icon>smartphone</md-icon><span>{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
|
||||
<md-icon>smartphone</md-icon>
|
||||
<span class="md-list-item-text">{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<div>
|
||||
@@ -84,7 +86,7 @@
|
||||
<p>Dump targets:</p>
|
||||
<md-checkbox v-for="file in DUMP_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox>
|
||||
</div>
|
||||
<div class="md-layout md-gutter">
|
||||
<div class="md-layout">
|
||||
<md-button class="md-accent md-raised" @click="startTrace">Start trace</md-button>
|
||||
<md-button class="md-accent md-raised" @click="dumpState">Dump state</md-button>
|
||||
<md-button class="md-raised" @click="resetLastDevice">Device list</md-button>
|
||||
@@ -100,14 +102,14 @@
|
||||
</md-card-content>
|
||||
<md-card-content v-if="status === STATES.END_TRACE">
|
||||
<span class="md-subheading">Tracing...</span>
|
||||
<md-progress md-indeterminate></md-progress>
|
||||
<div class="md-layout md-gutter">
|
||||
<md-progress-bar md-mode="indeterminate"></md-progress-bar>
|
||||
<div class="md-layout">
|
||||
<md-button class="md-accent md-raised" @click="endTrace">End trace</md-button>
|
||||
</div>
|
||||
</md-card-content>
|
||||
<md-card-content v-if="status === STATES.LOAD_DATA">
|
||||
<span class="md-subheading">Loading data...</span>
|
||||
<md-progress :md-progress="loadProgress"></md-progress>
|
||||
<md-progress-bar md-mode="determinate" :md-value="loadProgress"></md-progress-bar>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</template>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="bounds" v-if="visible">
|
||||
<md-field>
|
||||
<md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions"
|
||||
placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" >
|
||||
<md-option value="displayCreation, displayDeletion">Display</md-option>
|
||||
@@ -23,6 +24,7 @@
|
||||
<md-option value="vsyncEvent">vsync</md-option>
|
||||
<md-option value="bufferUpdate">Buffer</md-option>
|
||||
</md-select>
|
||||
</md-field>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@@ -37,15 +37,17 @@
|
||||
</div>
|
||||
<div class="md-layout">
|
||||
<div class="md-layout-item md-small-size-100">
|
||||
<md-field>
|
||||
<md-select v-model="fileType" id="file-type" placeholder="File type">
|
||||
<md-option value="auto">Detect type</md-option>
|
||||
<md-option :value="k" v-for="(v,k) in FILE_TYPES" v-bind:key="v.name">{{v.name}}</md-option>
|
||||
</md-select>
|
||||
</md-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-layout md-gutter">
|
||||
<input type="file" @change="onLoadFile" id="upload-file" v-show="false" />
|
||||
<label class="md-button md-accent md-raised md-theme-default" for="upload-file">Add File</label>
|
||||
<div class="md-layout">
|
||||
<input type="file" @change="onLoadFile" ref="fileUpload" v-show="false" />
|
||||
<md-button class="md-accent md-raised md-theme-default" @click="$refs.fileUpload.click()">Add File</md-button>
|
||||
<md-button v-if="dataReady" @click="onSubmit" class="md-button md-primary md-raised md-theme-default">Submit</md-button>
|
||||
</div>
|
||||
</md-card-content>
|
||||
|
||||
@@ -13,33 +13,32 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<template>
|
||||
<md-card v-if="file">
|
||||
<md-card v-if="(isLog(file) || isTrace(file))">
|
||||
<md-card-header>
|
||||
<md-card-header-text>
|
||||
<div class="md-title">
|
||||
<md-icon>{{file.type.icon}}</md-icon> {{file.filename}}
|
||||
<md-icon>{{file.type.icon}}</md-icon>
|
||||
{{file.filename}}
|
||||
</div>
|
||||
</md-card-header-text>
|
||||
<md-button :href="file.blobUrl" :download="file.filename" class="md-icon-button">
|
||||
<md-icon>save_alt</md-icon>
|
||||
</md-button>
|
||||
</md-card-header>
|
||||
<traceview v-if="isTrace" :store="store" :file="file" ref="view" />
|
||||
<videoview v-if="isVideo" :file="file" ref="view" />
|
||||
<logview v-if="isLog" :file="file" ref="view" />
|
||||
<div v-if="!(isTrace || isVideo || isLog)">
|
||||
<traceview v-if="isTrace(file)" :store="store" :file="file" ref="view" />
|
||||
<logview v-if="isLog(file)" :file="file" ref="view" />
|
||||
<div v-if="!(isTrace(file) || isVideo(file) || isLog(file))">
|
||||
<h1 class="bad">Unrecognized DataType</h1>
|
||||
</div>
|
||||
</md-card>
|
||||
</template>
|
||||
<script>
|
||||
import TraceView from './TraceView.vue'
|
||||
import VideoView from './VideoView.vue'
|
||||
import LogView from './LogView.vue'
|
||||
import { DATA_TYPES } from './decode.js'
|
||||
import TraceView from "./TraceView.vue";
|
||||
import LogView from "./LogView.vue";
|
||||
import FileType from "./FileType.js";
|
||||
|
||||
export default {
|
||||
name: 'dataview',
|
||||
name: "dataview",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
@@ -49,32 +48,15 @@ export default {
|
||||
},
|
||||
arrowDown() {
|
||||
return this.$refs.view.arrowDown();
|
||||
},
|
||||
},
|
||||
props: ['store', 'file'],
|
||||
computed: {
|
||||
isTrace() {
|
||||
return this.file.type == DATA_TYPES.WINDOW_MANAGER ||
|
||||
this.file.type == DATA_TYPES.SURFACE_FLINGER ||
|
||||
this.file.type == DATA_TYPES.TRANSACTION ||
|
||||
this.file.type == DATA_TYPES.WAYLAND ||
|
||||
this.file.type == DATA_TYPES.SYSTEM_UI ||
|
||||
this.file.type == DATA_TYPES.LAUNCHER
|
||||
},
|
||||
isVideo() {
|
||||
return this.file.type == DATA_TYPES.SCREEN_RECORDING;
|
||||
},
|
||||
isLog() {
|
||||
return this.file.type == DATA_TYPES.PROTO_LOG
|
||||
}
|
||||
},
|
||||
props: ["store", "file"],
|
||||
mixins: [FileType],
|
||||
components: {
|
||||
'traceview': TraceView,
|
||||
'videoview': VideoView,
|
||||
'logview': LogView,
|
||||
traceview: TraceView,
|
||||
logview: LogView
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.bad {
|
||||
|
||||
21
tools/winscope/src/FileType.js
Normal file
21
tools/winscope/src/FileType.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { DATA_TYPES } from './decode.js'
|
||||
|
||||
export default {
|
||||
name: 'FileType',
|
||||
methods: {
|
||||
isTrace: function (file) {
|
||||
return file.type == DATA_TYPES.WINDOW_MANAGER ||
|
||||
file.type == DATA_TYPES.SURFACE_FLINGER ||
|
||||
file.type == DATA_TYPES.TRANSACTION ||
|
||||
file.type == DATA_TYPES.WAYLAND ||
|
||||
file.type == DATA_TYPES.SYSTEM_UI ||
|
||||
file.type == DATA_TYPES.LAUNCHER
|
||||
},
|
||||
isVideo(file) {
|
||||
return file.type == DATA_TYPES.SCREEN_RECORDING;
|
||||
},
|
||||
isLog(file) {
|
||||
return file.type == DATA_TYPES.PROTO_LOG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,20 +15,20 @@
|
||||
<template>
|
||||
<md-card-content class="container">
|
||||
<md-table class="log-table">
|
||||
<md-table-header>
|
||||
<md-table-row>
|
||||
<md-table-head class="time-column-header">Time</md-table-head>
|
||||
<md-table-head class="tag-column-header">Tag</md-table-head>
|
||||
<md-table-head class="at-column-header">At</md-table-head>
|
||||
<md-table-head>Message</md-table-head>
|
||||
</md-table-header>
|
||||
<md-table-body>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row v-for="line in data" :key="line.timestamp">
|
||||
<md-table-cell class="time-column">{{line.time}}</md-table-cell>
|
||||
<md-table-cell class="tag-column">{{line.tag}}</md-table-cell>
|
||||
<md-table-cell class="at-column">{{line.at}}</md-table-cell>
|
||||
<md-table-cell>{{line.text}}</md-table-cell>
|
||||
</md-table-row>
|
||||
</md-table-body>
|
||||
|
||||
</md-table>
|
||||
</md-card-content>
|
||||
</template>
|
||||
|
||||
@@ -31,6 +31,7 @@ export default {
|
||||
props: ['bounds', 'rects', 'highlight'],
|
||||
data () {
|
||||
return {
|
||||
desiredHeight: 800,
|
||||
desiredWidth: 400,
|
||||
};
|
||||
},
|
||||
@@ -50,7 +51,13 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
s(sourceCoordinate) { // translate source into target coordinates
|
||||
return sourceCoordinate / this.boundsC.width * this.desiredWidth;
|
||||
var scale;
|
||||
if(this.boundsC.width < this.boundsC.height) {
|
||||
scale = this.desiredHeight / this.boundsC.height;
|
||||
} else {
|
||||
scale = this.desiredWidth / this.boundsC.width;
|
||||
}
|
||||
return sourceCoordinate * scale;
|
||||
},
|
||||
rectToStyle(r) {
|
||||
var x = this.s(r.left);
|
||||
@@ -82,10 +89,10 @@ export default {
|
||||
}
|
||||
.rect {
|
||||
border: 1px solid black;
|
||||
background-color: rgba(100, 100, 100, 0.8);
|
||||
background-color: rgba(110, 114, 116, 0.8);
|
||||
}
|
||||
.highlight {
|
||||
border: 2px solid red;
|
||||
border: 2px solid rgb(235, 52, 52);
|
||||
pointer-events: none;
|
||||
}
|
||||
.label {
|
||||
|
||||
@@ -13,17 +13,38 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<template>
|
||||
<svg width="2000" height="20" viewBox="-5,0,2010,20">
|
||||
<circle :cx="position(item)" cy="10" r="5" v-for="(item, idx) in items" @click="onItemClick(idx)" />
|
||||
<circle v-if="items.length" :cx="position(selected)" cy="10" r="5" class="selected" />
|
||||
<svg width="100%" height="20">
|
||||
<rect
|
||||
:x="position(item)"
|
||||
y="0"
|
||||
:width="pointWidth"
|
||||
:height="pointHeight"
|
||||
:rx="corner"
|
||||
v-for="(item, idx) in items"
|
||||
:key="item"
|
||||
@click="onItemClick(idx)"
|
||||
/>
|
||||
<rect
|
||||
v-if="items.length"
|
||||
:x="position(selected)"
|
||||
y="0"
|
||||
:width="pointWidth"
|
||||
:height="pointHeight"
|
||||
:rx="corner"
|
||||
class="selected"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'timeline',
|
||||
props: ['items', 'selectedIndex', 'scale'],
|
||||
name: "timeline",
|
||||
props: ["items", "selectedIndex", "scale"],
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
pointWidth: "1%",
|
||||
pointHeight: 15,
|
||||
corner: 2
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
position(item) {
|
||||
@@ -34,11 +55,11 @@ export default {
|
||||
if (scale[0] >= scale[1]) {
|
||||
return cx;
|
||||
}
|
||||
return (cx - scale[0]) / (scale[1] - scale[0]) * 2000;
|
||||
return (((cx - scale[0]) / (scale[1] - scale[0])) * 100) + "%";
|
||||
},
|
||||
onItemClick(index) {
|
||||
this.$emit('item-selected', index);
|
||||
},
|
||||
this.$emit("item-selected", index);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
timestamps() {
|
||||
@@ -50,13 +71,11 @@ export default {
|
||||
selected() {
|
||||
return this.items[this.selectedIndex];
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.selected {
|
||||
fill: red;
|
||||
fill: rgb(240, 59, 59);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -15,29 +15,29 @@
|
||||
<template>
|
||||
<md-card-content class="container">
|
||||
<md-card class="rects" v-if="hasScreenView">
|
||||
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<h2 class="md-title">Screen</h2>
|
||||
</md-whiteframe>
|
||||
<md-whiteframe md-elevation="8">
|
||||
</md-content>
|
||||
<md-content class="md-elevation-8">
|
||||
<rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" />
|
||||
</md-whiteframe>
|
||||
</md-content>
|
||||
</md-card>
|
||||
<md-card class="hierarchy">
|
||||
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<h2 class="md-title" style="flex: 1;">Hierarchy</h2>
|
||||
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
|
||||
<md-checkbox v-model="store.flattened">Flat</md-checkbox>
|
||||
<input id="filter" type="search" placeholder="Filter..." v-model="hierarchyPropertyFilterString" />
|
||||
</md-whiteframe>
|
||||
</md-content>
|
||||
<tree-view class="data-card" :item="tree" @item-selected="itemSelected" :selected="hierarchySelected" :filter="hierarchyFilter" :flattened="store.flattened" ref="hierarchy" />
|
||||
</md-card>
|
||||
<md-card class="properties">
|
||||
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<h2 class="md-title" style="flex: 1">Properties</h2>
|
||||
<div class="filter">
|
||||
<input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" />
|
||||
</div>
|
||||
</md-whiteframe>
|
||||
</md-content>
|
||||
<tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" />
|
||||
</md-card>
|
||||
</md-card-content>
|
||||
|
||||
@@ -14,28 +14,61 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="tree-view">
|
||||
<div @click="clicked" :class="computedClass">
|
||||
<span class="kind">{{item.kind}}</span><span v-if="item.kind && item.name"> - </span><span>{{item.name}}</span>
|
||||
<div v-for="c in item.chips" :title="c.long" :class="chipClassForChip(c)">
|
||||
{{c.short}}
|
||||
</div>
|
||||
<div @click="clicked" :class="computedClass" class="node">
|
||||
<span class="kind">{{item.kind}}</span>
|
||||
<span v-if="item.kind && item.name">-</span>
|
||||
<span>{{item.name}}</span>
|
||||
<div
|
||||
v-for="c in item.chips"
|
||||
v-bind:key="c.long"
|
||||
:title="c.long"
|
||||
:class="chipClassForChip(c)"
|
||||
>{{c.short}}</div>
|
||||
</div>
|
||||
<div class="children" v-if="children">
|
||||
<tree-view v-for="(c,i) in children" :item="c" @item-selected="childItemSelected" :selected="selected" :key="i" :chip-class='chipClass' :filter="childFilter(c)" :flattened="flattened" :force-flattened="applyingFlattened" v-show="filterMatches(c)" ref='children' />
|
||||
<tree-view
|
||||
v-for="(c,i) in children"
|
||||
:item="c"
|
||||
@item-selected="childItemSelected"
|
||||
:selected="selected"
|
||||
:key="i"
|
||||
:chip-class="chipClass"
|
||||
:filter="childFilter(c)"
|
||||
:flattened="flattened"
|
||||
:force-flattened="applyingFlattened"
|
||||
v-show="filterMatches(c)"
|
||||
ref="children"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
|
||||
import protobuf from 'protobufjs'
|
||||
import jsonProtoDefs from "frameworks/base/core/proto/android/server/windowmanagertrace.proto";
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs);
|
||||
var TraceMessage = protoDefs.lookupType(
|
||||
"com.android.server.wm.WindowManagerTraceFileProto"
|
||||
);
|
||||
var ServiceMessage = protoDefs.lookupType(
|
||||
"com.android.server.wm.WindowManagerServiceDumpProto"
|
||||
);
|
||||
|
||||
export default {
|
||||
name: 'tree-view',
|
||||
props: ['item', 'selected', 'chipClass', 'filter', 'flattened', 'force-flattened'],
|
||||
data() {
|
||||
return {};
|
||||
name: "tree-view",
|
||||
props: [
|
||||
"item",
|
||||
"selected",
|
||||
"chipClass",
|
||||
"filter",
|
||||
"flattened",
|
||||
"force-flattened"
|
||||
],
|
||||
data: function() {
|
||||
return {
|
||||
isChildSelected: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectNext(found, parent) {
|
||||
@@ -43,7 +76,7 @@ export default {
|
||||
this.clicked();
|
||||
return false;
|
||||
}
|
||||
if (this.selected === this.item) {
|
||||
if (this.isCurrentSelected()) {
|
||||
found = true;
|
||||
}
|
||||
if (this.$refs.children) {
|
||||
@@ -63,20 +96,23 @@ export default {
|
||||
this.clicked();
|
||||
return false;
|
||||
}
|
||||
if (this.selected === this.item) {
|
||||
if (this.isCurrentSelected()) {
|
||||
found = true;
|
||||
}
|
||||
return found;
|
||||
},
|
||||
childItemSelected(item) {
|
||||
this.$emit('item-selected', item);
|
||||
this.isChildSelected = true;
|
||||
this.$emit("item-selected", item);
|
||||
},
|
||||
clicked() {
|
||||
this.$emit('item-selected', this.item);
|
||||
this.$emit("item-selected", this.item);
|
||||
},
|
||||
chipClassForChip(c) {
|
||||
return ['tree-view-internal-chip', this.chipClassOrDefault,
|
||||
this.chipClassOrDefault + '-' + (c.class || 'default')
|
||||
return [
|
||||
"tree-view-internal-chip",
|
||||
this.chipClassOrDefault,
|
||||
this.chipClassOrDefault + "-" + (c.class || "default")
|
||||
];
|
||||
},
|
||||
filterMatches(c) {
|
||||
@@ -99,40 +135,72 @@ export default {
|
||||
}
|
||||
return this.filter;
|
||||
},
|
||||
isCurrentSelected() {
|
||||
return this.selected === this.item;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
computedClass() {
|
||||
return (this.item == this.selected) ? 'selected' : ''
|
||||
},
|
||||
chipClassOrDefault() {
|
||||
return this.chipClass || 'tree-view-chip';
|
||||
return this.chipClass || "tree-view-chip";
|
||||
},
|
||||
applyingFlattened() {
|
||||
return this.flattened && this.item.flattened || this.forceFlattened;
|
||||
return (this.flattened && this.item.flattened) || this.forceFlattened;
|
||||
},
|
||||
children() {
|
||||
return this.applyingFlattened ? this.item.flattened : this.item.children;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.data-card > .tree-view {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tree-view {
|
||||
display: block;
|
||||
border-left: 1px solid rgb(238, 238, 238);
|
||||
}
|
||||
|
||||
.tree-view.tree-view {
|
||||
margin: 0;
|
||||
padding: 1px 8px;
|
||||
}
|
||||
|
||||
.tree-view .node {
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-view .node:hover:not(.selected) {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.children {
|
||||
margin-left: 24px;
|
||||
margin-left: 8px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.kind {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #3f51b5;
|
||||
background-color: #365179;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.childSelected {
|
||||
border-left: 1px solid rgb(233, 22, 22)
|
||||
}
|
||||
|
||||
.selected .kind {
|
||||
color: #ccc;
|
||||
color: #e9e9e9;
|
||||
}
|
||||
|
||||
.tree-view-internal-chip {
|
||||
|
||||
@@ -13,16 +13,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<template>
|
||||
<md-card-content class="container">
|
||||
<md-card class="rects">
|
||||
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||
<h2 class="md-title">Screen</h2>
|
||||
</md-whiteframe>
|
||||
<md-whiteframe md-elevation="8">
|
||||
<video :id="file.filename" class="screen" :src="file.data" />
|
||||
</md-whiteframe>
|
||||
</md-card>
|
||||
</md-card-content>
|
||||
<video class="md-elevation-2 screen" :id="file.filename" :src="file.data" />
|
||||
</template>
|
||||
<script>
|
||||
const EPSILON = 0.00001
|
||||
@@ -70,6 +61,6 @@ export default {
|
||||
<style>
|
||||
.screen {
|
||||
max-height: 50em;
|
||||
max-width: 50em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html lang="en">
|
||||
<html lang="en" class="md-scrollbar">
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="icon" type="image/svg" href="static/favicon.svg"/>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<title>winscope</title>
|
||||
|
||||
@@ -18,6 +18,8 @@ import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
import 'style-loader!css-loader!vue-material/dist/vue-material.css'
|
||||
import 'style-loader!css-loader!vue-material/dist/theme/default.css'
|
||||
|
||||
import VueMaterial from 'vue-material'
|
||||
Vue.use(VueMaterial);
|
||||
|
||||
|
||||
134
tools/winscope/static/favicon.svg
Normal file
134
tools/winscope/static/favicon.svg
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46667"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="winscope.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient144">
|
||||
<stop
|
||||
style="stop-color:#3ddb85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop140" />
|
||||
<stop
|
||||
style="stop-color:#3ddb85;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop142" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient144"
|
||||
id="linearGradient146"
|
||||
x1="334.86557"
|
||||
y1="288.25122"
|
||||
x2="362.64478"
|
||||
y2="405.42892"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient144"
|
||||
id="linearGradient152"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="334.86557"
|
||||
y1="288.25122"
|
||||
x2="362.64478"
|
||||
y2="405.42892" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="369.78115"
|
||||
inkscape:cy="43.694507"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1399"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="20"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline"
|
||||
transform="translate(0,-161.53331)">
|
||||
<g
|
||||
id="g60"
|
||||
transform="matrix(1.4161793,0,0,1.4161793,-266.89731,76.345061)">
|
||||
<path
|
||||
id="path46"
|
||||
d="m 254.98635,139.13116 c -4.3276,0 -7.84784,-3.71395 -7.84784,-8.27594 0,-4.56195 4.14824,-8.27278 7.84784,-8.27285 3.6996,-9e-5 7.84855,3.71014 7.84855,8.27285 0,4.56276 -3.52025,8.27594 -7.84855,8.27594 m -36.93749,-2.38905 c -3.0836,0 -5.59192,-2.64634 -5.59192,-5.89694 0,-3.25059 2.50784,-5.89476 5.59192,-5.89476 3.08358,0 5.5919,2.64364 5.5919,5.89476 0,3.25114 -2.50781,5.89694 -5.5919,5.89694 m 52.09761,-36.05821 11.17666,-20.405632 c 0.64058,-1.172551 0.25981,-2.670449 -0.85112,-3.348238 -1.11236,-0.674958 -2.53307,-0.273633 -3.17621,0.897219 l -11.31638,20.663525 c -8.6545,-4.162927 -18.37356,-6.482308 -28.74463,-6.482308 -10.37108,0 -20.09013,2.319381 -28.74411,6.482308 L 197.17379,77.827249 c -0.64314,-1.170852 -2.06385,-1.572177 -3.17621,-0.897219 -1.11167,0.677199 -1.49261,2.175808 -0.85113,3.348238 L 204.3231,100.6839 c -19.19192,11.00346 -32.318,31.48516 -34.23828,55.68305 h 134.29993 c -1.9218,-24.19736 -15.04789,-44.67959 -34.23828,-55.68357"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3ddb85;stroke-width:0.52547503"
|
||||
sodipodi:nodetypes="csssccsssccccccscccccccc" />
|
||||
<path
|
||||
id="path48"
|
||||
d="M -0.75490308,-4.3032616 H 466.2911 V 245.86074 H -0.75490308 Z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2"
|
||||
transform="translate(0,-161.53331)"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="g150"
|
||||
transform="matrix(1.2390981,0,0,1.2745122,-22.278191,-60.145091)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="font-weight:400;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;display:inline;overflow:visible;opacity:0.92156863;isolation:auto;mix-blend-mode:normal;fill:#000000;stroke-width:3.05656147"
|
||||
d="m 93.327473,227.20793 c -11.817096,0 -21.462353,9.58571 -21.462353,21.32967 0,11.74397 9.645257,21.32967 21.462353,21.32967 10.773667,0 19.717027,-7.97589 21.216847,-18.28256 h 2.56476 c 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 h -2.56476 c -1.49999,-10.3067 -10.44318,-18.28259 -21.216847,-18.28259 z m 0,6.09419 c 8.503037,0 15.330367,6.78508 15.330367,15.23548 0,8.45041 -6.82733,15.2355 -15.330367,15.2355 -8.503052,0 -15.330212,-6.78509 -15.330212,-15.2355 0,-8.45041 6.82716,-15.23548 15.330212,-15.23548 z m 23.781607,21.32968 c -1.69335,-2e-5 -3.06609,1.36422 -3.06609,3.0471 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 0,-1.68287 -1.37273,-3.04711 -3.06607,-3.0471 z m 0,9.1413 c -1.69334,-2e-5 -3.06607,1.3642 -3.06609,3.04706 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -2e-5,-1.68286 -1.37274,-3.04707 -3.06607,-3.04706 z m 0,9.14127 c -1.69335,-2e-5 -3.06609,1.36421 -3.06609,3.04709 0,1.68288 1.37274,3.04712 3.06609,3.0471 1.69334,1e-5 3.06607,-1.36423 3.06607,-3.0471 0,-1.68287 -1.37273,-3.0471 -3.06607,-3.04709 z m 0,9.14129 c -1.69334,-2e-5 -3.06608,1.36421 -3.06609,3.04708 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 z"
|
||||
font-weight="400"
|
||||
overflow="visible"
|
||||
id="path62"
|
||||
sodipodi:nodetypes="sssccsccsssssscccccccccccscsccscsc" />
|
||||
<path
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,161.53331)"
|
||||
sodipodi:nodetypes="sssssscssscs"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path130"
|
||||
d="m 346.52914,385.88304 c -9.74153,-1.15668 -19.18712,-4.7389 -27.36682,-10.3788 -4.29704,-2.96281 -11.19439,-9.96762 -14.26053,-14.48272 -3.09123,-4.55204 -6.34453,-11.66167 -7.89403,-17.2513 -1.63959,-5.91462 -2.25943,-17.8322 -1.2539,-24.10849 3.61484,-22.56303 20.01002,-40.31938 42.90934,-46.47177 4.28814,-1.1521 5.83599,-1.30305 13.67432,-1.33348 9.26661,-0.036 11.69398,0.2988 18.66447,2.5742 8.55206,2.79168 15.83504,7.31293 22.56577,14.00876 20.25076,20.14579 22.48959,52.06917 5.25566,74.94032 -8.89686,11.80702 -22.47121,19.85596 -37.24125,22.08226 -4.528,0.6825 -11.2641,0.87091 -15.05303,0.42102 z"
|
||||
style="opacity:0.49673205;fill:url(#linearGradient152);fill-opacity:1;stroke:none;stroke-width:0.50507629" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
5
tools/winscope/vue.config.js
Normal file
5
tools/winscope/vue.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
configureWebpack: {
|
||||
devtool: 'source-map'
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user