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:
Pablo Gamito
2020-06-04 08:17:43 +00:00
committed by Android (Google) Code Review
19 changed files with 1167 additions and 788 deletions

View File

@@ -2,3 +2,4 @@ node_modules/
adb_proxy/venv/ adb_proxy/venv/
.vscode/ .vscode/
dist/ dist/
yarn-error.log

View File

@@ -10,13 +10,13 @@
}, },
"dependencies": { "dependencies": {
"vue": "^2.3.3", "vue": "^2.3.3",
"vue-material": "0.8.1" "vue-material": "^1.0.0-beta-11"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.0.0", "babel-core": "^6.0.0",
"babel-loader": "^6.0.0", "babel-loader": "^6.0.0",
"babel-preset-env": "^1.5.1", "babel-preset-env": "^1.5.1",
"cross-env": "^3.0.0", "cross-env": "^7.0.2",
"css-loader": "^0.25.0", "css-loader": "^0.25.0",
"file-loader": "^0.9.0", "file-loader": "^0.9.0",
"html-webpack-inline-source-plugin": "^0.0.9", "html-webpack-inline-source-plugin": "^0.0.9",

View File

@@ -14,29 +14,59 @@
--> -->
<template> <template>
<div id="app"> <div id="app">
<md-whiteframe md-tag="md-toolbar"> <md-app>
<h1 class="md-title" style="flex: 1">{{title}}</h1> <md-app-toolbar md-tag="md-toolbar">
<a class="md-button md-accent md-raised md-theme-default" @click="clear()" v-if="dataLoaded">Clear</a> <h1 class="md-title" style="flex: 1">{{title}}</h1>
</md-whiteframe> <md-button
<div class="main-content"> class="md-accent md-raised md-theme-default"
<md-layout v-if="!dataLoaded" class="m-2"> @click="clear()"
<dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/> v-if="dataLoaded"
<datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/> >Clear</md-button>
</md-layout> </md-app-toolbar>
<md-card v-if="dataLoaded"> <md-app-content class="main-content">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense"> <div class="md-layout m-2" v-if="!dataLoaded">
<h2 class="md-title">Timeline</h2> <dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
<datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" /> <datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
</md-whiteframe> </div>
<md-list> <div class="md-layout md-alignment-left-center " v-if="dataLoaded">
<md-list-item v-for="(file, idx) in files" :key="file.filename"> <div class="md-layout-item video" v-if="video">
<md-icon>{{file.type.icon}}</md-icon> <videoview :file="video" :ref="video.filename" />
<timeline :items="file.timeline" :selected-index="file.selectedIndex" :scale="scale" @item-selected="onTimelineItemSelected($event, idx)" class="timeline" /> </div>
</md-list-item> <div class="md-layout-item">
</md-list> <md-toolbar
</md-card> md-elevation="0"
<dataview v-for="file in files" :key="file.filename" :ref="file.filename" :store="store" :file="file" @focus="onDataViewFocus(file.filename)" /> class="md-transparent">
</div> <h2 class="md-title">Timeline </h2>
<span> {{seekTime}} </span>
<datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" />
</md-toolbar>
<md-list>
<md-list-item v-for="(file, idx) in files" :key="file.filename">
<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>
</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> </div>
</template> </template>
<script> <script>
@@ -44,10 +74,13 @@ import TreeView from './TreeView.vue'
import Timeline from './Timeline.vue' import Timeline from './Timeline.vue'
import Rects from './Rects.vue' import Rects from './Rects.vue'
import DataView from './DataView.vue' import DataView from './DataView.vue'
import VideoView from './VideoView.vue'
import DataInput from './DataInput.vue' import DataInput from './DataInput.vue'
import LocalStore from './localstore.js' import LocalStore from './localstore.js'
import DataAdb from './DataAdb.vue' import DataAdb from './DataAdb.vue'
import DataFilter from './DataFilter.vue' import DataFilter from './DataFilter.vue'
import FileType from './FileType.js'
import { nanos_to_string } from './transform.js'
const APP_NAME = "Winscope" const APP_NAME = "Winscope"
@@ -68,9 +101,11 @@ function findLastMatchingSorted(array, predicate) {
export default { export default {
name: 'app', name: 'app',
mixins: [FileType],
data() { data() {
return { return {
files: [], files: [],
video: null,
title: APP_NAME, title: APP_NAME,
currentTimestamp: 0, currentTimestamp: 0,
activeDataView: null, activeDataView: null,
@@ -143,7 +178,13 @@ export default {
return true; return true;
}, },
onDataReady(files) { 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) { setStatus(status) {
if (status) { if (status) {
@@ -151,7 +192,7 @@ export default {
} else { } else {
this.title = APP_NAME; this.title = APP_NAME;
} }
} },
}, },
computed: { computed: {
prettyDump: function() { return JSON.stringify(this.dump, null, 2); }, prettyDump: function() { return JSON.stringify(this.dump, null, 2); },
@@ -166,6 +207,9 @@ export default {
this.activeDataView = this.files[0].filename; this.activeDataView = this.files[0].filename;
} }
return this.activeDataView; return this.activeDataView;
},
seekTime: function() {
return nanos_to_string(this.currentTimestamp);
} }
}, },
watch: { watch: {
@@ -174,14 +218,14 @@ export default {
} }
}, },
components: { components: {
'timeline': Timeline, timeline: Timeline,
'dataview': DataView, dataview: DataView,
'datainput': DataInput, videoview: VideoView,
'dataadb': DataAdb, datainput: DataInput,
'datafilter': DataFilter, dataadb: DataAdb,
}, datafilter: DataFilter
} }
};
</script> </script>
<style> <style>
.main-content>* { .main-content>* {
@@ -201,14 +245,14 @@ export default {
flex-wrap: wrap; flex-wrap: wrap;
} }
.md-layout > .md-card {
margin: 0.5em;
}
.md-button { .md-button {
margin-top: 1em margin-top: 1em
} }
.video {
flex-grow: 0;
}
h1, h1,
h2 { h2 {
font-weight: normal; font-weight: normal;
@@ -227,5 +271,4 @@ li {
a { a {
color: #42b983; color: #42b983;
} }
</style> </style>

View File

@@ -18,7 +18,7 @@
<div class="md-title">ADB Connect</div> <div class="md-title">ADB Connect</div>
</md-card-header> </md-card-header>
<md-card-content v-if="status === STATES.CONNECTING"> <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>
<md-card-content v-if="status === STATES.NO_PROXY"> <md-card-content v-if="status === STATES.NO_PROXY">
<md-icon class="md-accent">error</md-icon> <md-icon class="md-accent">error</md-icon>
@@ -52,10 +52,10 @@
<md-card-content v-if="status === STATES.UNAUTH"> <md-card-content v-if="status === STATES.UNAUTH">
<md-icon class="md-accent">lock</md-icon> <md-icon class="md-accent">lock</md-icon>
<span class="md-subheading">Proxy authorisation required</span> <span class="md-subheading">Proxy authorisation required</span>
<md-input-container> <md-field>
<label>Enter Winscope proxy token</label> <label>Enter Winscope proxy token</label>
<md-input v-model="adbStore.proxyKey"></md-input> <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-body-2">The proxy token is printed to console on proxy launch, copy and paste it above.</div>
<div class="md-layout md-gutter"> <div class="md-layout md-gutter">
<md-button class="md-accent md-raised" @click="restart">Connect</md-button> <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> <div class="md-subheading">{{ Object.keys(devices).length > 0 ? "Connected devices:" : "No devices detected" }}</div>
<md-list> <md-list>
<md-list-item v-for="(device, id) in devices" :key="id" @click="selectDevice(id)" :disabled="!device.authorised"> <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-item>
</md-list> </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>
<md-card-content v-if="status === STATES.START_TRACE"> <md-card-content v-if="status === STATES.START_TRACE">
<md-list> <md-list>
<md-list-item> <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-item>
</md-list> </md-list>
<div> <div>
@@ -84,7 +86,7 @@
<p>Dump targets:</p> <p>Dump targets:</p>
<md-checkbox v-for="file in DUMP_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox> <md-checkbox v-for="file in DUMP_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox>
</div> </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="startTrace">Start trace</md-button>
<md-button class="md-accent md-raised" @click="dumpState">Dump state</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> <md-button class="md-raised" @click="resetLastDevice">Device list</md-button>
@@ -100,14 +102,14 @@
</md-card-content> </md-card-content>
<md-card-content v-if="status === STATES.END_TRACE"> <md-card-content v-if="status === STATES.END_TRACE">
<span class="md-subheading">Tracing...</span> <span class="md-subheading">Tracing...</span>
<md-progress md-indeterminate></md-progress> <md-progress-bar md-mode="indeterminate"></md-progress-bar>
<div class="md-layout md-gutter"> <div class="md-layout">
<md-button class="md-accent md-raised" @click="endTrace">End trace</md-button> <md-button class="md-accent md-raised" @click="endTrace">End trace</md-button>
</div> </div>
</md-card-content> </md-card-content>
<md-card-content v-if="status === STATES.LOAD_DATA"> <md-card-content v-if="status === STATES.LOAD_DATA">
<span class="md-subheading">Loading data...</span> <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-content>
</md-card> </md-card>
</template> </template>

View File

@@ -14,15 +14,17 @@
--> -->
<template> <template>
<div class="bounds" v-if="visible"> <div class="bounds" v-if="visible">
<md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions" <md-field>
placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" > <md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions"
<md-option value="displayCreation, displayDeletion">Display</md-option> placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" >
<md-option value="powerModeUpdate">Power Mode</md-option> <md-option value="displayCreation, displayDeletion">Display</md-option>
<md-option value="surfaceCreation, surfaceDeletion">Surface</md-option> <md-option value="powerModeUpdate">Power Mode</md-option>
<md-option value="transaction">Transaction</md-option> <md-option value="surfaceCreation, surfaceDeletion">Surface</md-option>
<md-option value="vsyncEvent">vsync</md-option> <md-option value="transaction">Transaction</md-option>
<md-option value="bufferUpdate">Buffer</md-option> <md-option value="vsyncEvent">vsync</md-option>
</md-select> <md-option value="bufferUpdate">Buffer</md-option>
</md-select>
</md-field>
</div> </div>
</template> </template>
<script> <script>

View File

@@ -37,15 +37,17 @@
</div> </div>
<div class="md-layout"> <div class="md-layout">
<div class="md-layout-item md-small-size-100"> <div class="md-layout-item md-small-size-100">
<md-field>
<md-select v-model="fileType" id="file-type" placeholder="File type"> <md-select v-model="fileType" id="file-type" placeholder="File type">
<md-option value="auto">Detect type</md-option> <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-option :value="k" v-for="(v,k) in FILE_TYPES" v-bind:key="v.name">{{v.name}}</md-option>
</md-select> </md-select>
</md-field>
</div> </div>
</div> </div>
<div class="md-layout md-gutter"> <div class="md-layout">
<input type="file" @change="onLoadFile" id="upload-file" v-show="false" /> <input type="file" @change="onLoadFile" ref="fileUpload" v-show="false" />
<label class="md-button md-accent md-raised md-theme-default" for="upload-file">Add File</label> <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> <md-button v-if="dataReady" @click="onSubmit" class="md-button md-primary md-raised md-theme-default">Submit</md-button>
</div> </div>
</md-card-content> </md-card-content>

View File

@@ -13,33 +13,32 @@
limitations under the License. limitations under the License.
--> -->
<template> <template>
<md-card v-if="file"> <md-card v-if="(isLog(file) || isTrace(file))">
<md-card-header> <md-card-header>
<md-card-header-text> <md-card-header-text>
<div class="md-title"> <div class="md-title">
<md-icon>{{file.type.icon}}</md-icon> {{file.filename}} <md-icon>{{file.type.icon}}</md-icon>
{{file.filename}}
</div> </div>
</md-card-header-text> </md-card-header-text>
<md-button :href="file.blobUrl" :download="file.filename" class="md-icon-button"> <md-button :href="file.blobUrl" :download="file.filename" class="md-icon-button">
<md-icon>save_alt</md-icon> <md-icon>save_alt</md-icon>
</md-button> </md-button>
</md-card-header> </md-card-header>
<traceview v-if="isTrace" :store="store" :file="file" ref="view" /> <traceview v-if="isTrace(file)" :store="store" :file="file" ref="view" />
<videoview v-if="isVideo" :file="file" ref="view" /> <logview v-if="isLog(file)" :file="file" ref="view" />
<logview v-if="isLog" :file="file" ref="view" /> <div v-if="!(isTrace(file) || isVideo(file) || isLog(file))">
<div v-if="!(isTrace || isVideo || isLog)">
<h1 class="bad">Unrecognized DataType</h1> <h1 class="bad">Unrecognized DataType</h1>
</div> </div>
</md-card> </md-card>
</template> </template>
<script> <script>
import TraceView from './TraceView.vue' import TraceView from "./TraceView.vue";
import VideoView from './VideoView.vue' import LogView from "./LogView.vue";
import LogView from './LogView.vue' import FileType from "./FileType.js";
import { DATA_TYPES } from './decode.js'
export default { export default {
name: 'dataview', name: "dataview",
data() { data() {
return {} return {}
}, },
@@ -49,32 +48,15 @@ export default {
}, },
arrowDown() { arrowDown() {
return this.$refs.view.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: { components: {
'traceview': TraceView, traceview: TraceView,
'videoview': VideoView, logview: LogView
'logview': LogView,
} }
} };
</script> </script>
<style> <style>
.bad { .bad {

View 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
}
}
}

View File

@@ -15,20 +15,20 @@
<template> <template>
<md-card-content class="container"> <md-card-content class="container">
<md-table class="log-table"> <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="time-column-header">Time</md-table-head>
<md-table-head class="tag-column-header">Tag</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 class="at-column-header">At</md-table-head>
<md-table-head>Message</md-table-head> <md-table-head>Message</md-table-head>
</md-table-header> </md-table-row>
<md-table-body>
<md-table-row v-for="line in data" :key="line.timestamp"> <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="time-column">{{line.time}}</md-table-cell>
<md-table-cell class="tag-column">{{line.tag}}</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 class="at-column">{{line.at}}</md-table-cell>
<md-table-cell>{{line.text}}</md-table-cell> <md-table-cell>{{line.text}}</md-table-cell>
</md-table-row> </md-table-row>
</md-table-body>
</md-table> </md-table>
</md-card-content> </md-card-content>
</template> </template>

View File

@@ -31,6 +31,7 @@ export default {
props: ['bounds', 'rects', 'highlight'], props: ['bounds', 'rects', 'highlight'],
data () { data () {
return { return {
desiredHeight: 800,
desiredWidth: 400, desiredWidth: 400,
}; };
}, },
@@ -50,7 +51,13 @@ export default {
}, },
methods: { methods: {
s(sourceCoordinate) { // translate source into target coordinates 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) { rectToStyle(r) {
var x = this.s(r.left); var x = this.s(r.left);
@@ -82,10 +89,10 @@ export default {
} }
.rect { .rect {
border: 1px solid black; border: 1px solid black;
background-color: rgba(100, 100, 100, 0.8); background-color: rgba(110, 114, 116, 0.8);
} }
.highlight { .highlight {
border: 2px solid red; border: 2px solid rgb(235, 52, 52);
pointer-events: none; pointer-events: none;
} }
.label { .label {

View File

@@ -13,17 +13,38 @@
limitations under the License. limitations under the License.
--> -->
<template> <template>
<svg width="2000" height="20" viewBox="-5,0,2010,20"> <svg width="100%" height="20">
<circle :cx="position(item)" cy="10" r="5" v-for="(item, idx) in items" @click="onItemClick(idx)" /> <rect
<circle v-if="items.length" :cx="position(selected)" cy="10" r="5" class="selected" /> :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> </svg>
</template> </template>
<script> <script>
export default { export default {
name: 'timeline', name: "timeline",
props: ['items', 'selectedIndex', 'scale'], props: ["items", "selectedIndex", "scale"],
data() { data() {
return {}; return {
pointWidth: "1%",
pointHeight: 15,
corner: 2
};
}, },
methods: { methods: {
position(item) { position(item) {
@@ -34,11 +55,11 @@ export default {
if (scale[0] >= scale[1]) { if (scale[0] >= scale[1]) {
return cx; return cx;
} }
return (cx - scale[0]) / (scale[1] - scale[0]) * 2000; return (((cx - scale[0]) / (scale[1] - scale[0])) * 100) + "%";
}, },
onItemClick(index) { onItemClick(index) {
this.$emit('item-selected', index); this.$emit("item-selected", index);
}, }
}, },
computed: { computed: {
timestamps() { timestamps() {
@@ -50,13 +71,11 @@ export default {
selected() { selected() {
return this.items[this.selectedIndex]; return this.items[this.selectedIndex];
} }
}, }
} };
</script> </script>
<style scoped> <style scoped>
.selected { .selected {
fill: red; fill: rgb(240, 59, 59);
} }
</style> </style>

View File

@@ -15,29 +15,29 @@
<template> <template>
<md-card-content class="container"> <md-card-content class="container">
<md-card class="rects" v-if="hasScreenView"> <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> <h2 class="md-title">Screen</h2>
</md-whiteframe> </md-content>
<md-whiteframe md-elevation="8"> <md-content class="md-elevation-8">
<rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" /> <rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" />
</md-whiteframe> </md-content>
</md-card> </md-card>
<md-card class="hierarchy"> <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> <h2 class="md-title" style="flex: 1;">Hierarchy</h2>
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox> <md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
<md-checkbox v-model="store.flattened">Flat</md-checkbox> <md-checkbox v-model="store.flattened">Flat</md-checkbox>
<input id="filter" type="search" placeholder="Filter..." v-model="hierarchyPropertyFilterString" /> <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" /> <tree-view class="data-card" :item="tree" @item-selected="itemSelected" :selected="hierarchySelected" :filter="hierarchyFilter" :flattened="store.flattened" ref="hierarchy" />
</md-card> </md-card>
<md-card class="properties"> <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> <h2 class="md-title" style="flex: 1">Properties</h2>
<div class="filter"> <div class="filter">
<input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" /> <input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" />
</div> </div>
</md-whiteframe> </md-content>
<tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" /> <tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" />
</md-card> </md-card>
</md-card-content> </md-card-content>

View File

@@ -14,28 +14,61 @@
--> -->
<template> <template>
<div class="tree-view"> <div class="tree-view">
<div @click="clicked" :class="computedClass"> <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> <span class="kind">{{item.kind}}</span>
<div v-for="c in item.chips" :title="c.long" :class="chipClassForChip(c)"> <span v-if="item.kind && item.name">-</span>
{{c.short}} <span>{{item.name}}</span>
</div> <div
v-for="c in item.chips"
v-bind:key="c.long"
:title="c.long"
:class="chipClassForChip(c)"
>{{c.short}}</div>
</div> </div>
<div class="children" v-if="children"> <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>
</div> </div>
</template> </template>
<script> <script>
import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto' import jsonProtoDefs from "frameworks/base/core/proto/android/server/windowmanagertrace.proto";
import protobuf from 'protobufjs' import protobuf from "protobufjs";
var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs); 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 { export default {
name: 'tree-view', name: "tree-view",
props: ['item', 'selected', 'chipClass', 'filter', 'flattened', 'force-flattened'], props: [
data() { "item",
return {}; "selected",
"chipClass",
"filter",
"flattened",
"force-flattened"
],
data: function() {
return {
isChildSelected: false
};
}, },
methods: { methods: {
selectNext(found, parent) { selectNext(found, parent) {
@@ -43,7 +76,7 @@ export default {
this.clicked(); this.clicked();
return false; return false;
} }
if (this.selected === this.item) { if (this.isCurrentSelected()) {
found = true; found = true;
} }
if (this.$refs.children) { if (this.$refs.children) {
@@ -63,20 +96,23 @@ export default {
this.clicked(); this.clicked();
return false; return false;
} }
if (this.selected === this.item) { if (this.isCurrentSelected()) {
found = true; found = true;
} }
return found; return found;
}, },
childItemSelected(item) { childItemSelected(item) {
this.$emit('item-selected', item); this.isChildSelected = true;
this.$emit("item-selected", item);
}, },
clicked() { clicked() {
this.$emit('item-selected', this.item); this.$emit("item-selected", this.item);
}, },
chipClassForChip(c) { chipClassForChip(c) {
return ['tree-view-internal-chip', this.chipClassOrDefault, return [
this.chipClassOrDefault + '-' + (c.class || 'default') "tree-view-internal-chip",
this.chipClassOrDefault,
this.chipClassOrDefault + "-" + (c.class || "default")
]; ];
}, },
filterMatches(c) { filterMatches(c) {
@@ -99,40 +135,72 @@ export default {
} }
return this.filter; return this.filter;
}, },
isCurrentSelected() {
return this.selected === this.item;
},
}, },
computed: { computed: {
computedClass() { computedClass() {
return (this.item == this.selected) ? 'selected' : '' return (this.item == this.selected) ? 'selected' : ''
}, },
chipClassOrDefault() { chipClassOrDefault() {
return this.chipClass || 'tree-view-chip'; return this.chipClass || "tree-view-chip";
}, },
applyingFlattened() { applyingFlattened() {
return this.flattened && this.item.flattened || this.forceFlattened; return (this.flattened && this.item.flattened) || this.forceFlattened;
}, },
children() { children() {
return this.applyingFlattened ? this.item.flattened : this.item.children; return this.applyingFlattened ? this.item.flattened : this.item.children;
}, },
} }
} };
</script> </script>
<style> <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 { .children {
margin-left: 24px; margin-left: 8px;
margin-top: 0px;
} }
.kind { .kind {
color: #333; color: #333;
font-weight: bold;
} }
.selected { .selected {
background-color: #3f51b5; background-color: #365179;
color: white; color: white;
} }
.childSelected {
border-left: 1px solid rgb(233, 22, 22)
}
.selected .kind { .selected .kind {
color: #ccc; color: #e9e9e9;
} }
.tree-view-internal-chip { .tree-view-internal-chip {

View File

@@ -13,16 +13,7 @@
limitations under the License. limitations under the License.
--> -->
<template> <template>
<md-card-content class="container"> <video class="md-elevation-2 screen" :id="file.filename" :src="file.data" />
<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>
</template> </template>
<script> <script>
const EPSILON = 0.00001 const EPSILON = 0.00001
@@ -70,6 +61,6 @@ export default {
<style> <style>
.screen { .screen {
max-height: 50em; max-height: 50em;
max-width: 50em;
} }
</style> </style>

View File

@@ -13,10 +13,11 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<html lang="en"> <html lang="en" class="md-scrollbar">
<head> <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/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <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"> <meta charset="utf-8">
<title>winscope</title> <title>winscope</title>

View File

@@ -18,6 +18,8 @@ import Vue from 'vue'
import App from './App.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/vue-material.css'
import 'style-loader!css-loader!vue-material/dist/theme/default.css'
import VueMaterial from 'vue-material' import VueMaterial from 'vue-material'
Vue.use(VueMaterial); Vue.use(VueMaterial);

View 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

View File

@@ -0,0 +1,5 @@
module.exports = {
configureWebpack: {
devtool: 'source-map'
}
}

File diff suppressed because it is too large Load Diff