1<!-- Copyright (C) 2017 The Android Open Source Project 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14--> 15<template> 16 <div id="app"> 17 <md-app> 18 <md-app-toolbar md-tag="md-toolbar" class="top-toolbar"> 19 <h1 class="md-title" style="flex: 1">{{title}}</h1> 20 <md-button 21 class="md-primary md-theme-default download-all-btn" 22 @click="downloadAsZip(files)" 23 v-if="dataLoaded" 24 >Download All</md-button> 25 <md-button 26 class="md-accent md-raised md-theme-default clear-btn" 27 style="box-shadow: none;" 28 @click="clear()" 29 v-if="dataLoaded" 30 >Clear</md-button> 31 </md-app-toolbar> 32 33 <md-app-content class="main-content" :style="mainContentStyle"> 34 <section class="data-inputs" v-if="!dataLoaded"> 35 <div class="input"> 36 <dataadb class="adbinput" ref="adb" :store="store" 37 @dataReady="onDataReady" @statusChange="setStatus" /> 38 </div> 39 <div class="input"> 40 <datainput class="fileinput" ref="input" :store="store" 41 @dataReady="onDataReady" @statusChange="setStatus" /> 42 </div> 43 </section> 44 45 <section class="data-view"> 46 <div 47 class="data-view-container" 48 v-for="file in dataViewFiles" 49 :key="file.type" 50 > 51 <dataview 52 :ref="file.type" 53 :store="store" 54 :file="file" 55 @click="onDataViewFocus(file)" 56 /> 57 </div> 58 59 <overlay 60 :store="store" 61 :ref="overlayRef" 62 v-if="dataLoaded" 63 v-on:bottom-nav-height-change="handleBottomNavHeightChange" 64 /> 65 </section> 66 </md-app-content> 67 </md-app> 68 </div> 69</template> 70<script> 71import Overlay from './Overlay.vue'; 72import DataView from './DataView.vue'; 73import DataInput from './DataInput.vue'; 74import LocalStore from './localstore.js'; 75import DataAdb from './DataAdb.vue'; 76import FileType from './mixins/FileType.js'; 77import SaveAsZip from './mixins/SaveAsZip'; 78import FocusedDataViewFinder from './mixins/FocusedDataViewFinder'; 79import {DIRECTION} from './utils/utils'; 80import {NAVIGATION_STYLE} from './utils/consts'; 81 82const APP_NAME = 'Winscope'; 83 84const CONTENT_BOTTOM_PADDING = 25; 85 86export default { 87 name: 'app', 88 mixins: [FileType, SaveAsZip, FocusedDataViewFinder], 89 data() { 90 return { 91 title: APP_NAME, 92 activeDataView: null, 93 // eslint-disable-next-line new-cap 94 store: LocalStore('app', { 95 flattened: false, 96 onlyVisible: false, 97 simplifyNames: true, 98 displayDefaults: true, 99 navigationStyle: NAVIGATION_STYLE.GLOBAL, 100 }), 101 overlayRef: 'overlay', 102 mainContentStyle: { 103 'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`, 104 }, 105 }; 106 }, 107 created() { 108 window.addEventListener('keydown', this.onKeyDown); 109 window.addEventListener('scroll', this.onScroll); 110 document.title = this.title; 111 }, 112 destroyed() { 113 window.removeEventListener('keydown', this.onKeyDown); 114 window.removeEventListener('scroll', this.onScroll); 115 }, 116 methods: { 117 clear() { 118 this.$store.commit('clearFiles'); 119 }, 120 onDataViewFocus(file) { 121 this.$store.commit('setActiveFile', file); 122 this.activeDataView = file.type; 123 }, 124 onKeyDown(event) { 125 event = event || window.event; 126 if (event.keyCode == 37 /* left */ ) { 127 this.$store.dispatch('advanceTimeline', DIRECTION.BACKWARD); 128 } else if (event.keyCode == 39 /* right */ ) { 129 this.$store.dispatch('advanceTimeline', DIRECTION.FORWARD); 130 } else if (event.keyCode == 38 /* up */ ) { 131 this.$refs[this.activeView][0].arrowUp(); 132 } else if (event.keyCode == 40 /* down */ ) { 133 this.$refs[this.activeView][0].arrowDown(); 134 } else { 135 return false; 136 } 137 event.preventDefault(); 138 return true; 139 }, 140 onDataReady(files) { 141 this.$store.dispatch('setFiles', files); 142 this.updateFocusedView(); 143 }, 144 setStatus(status) { 145 if (status) { 146 this.title = status; 147 } else { 148 this.title = APP_NAME; 149 } 150 }, 151 handleBottomNavHeightChange(newHeight) { 152 this.$set( 153 this.mainContentStyle, 154 'padding-bottom', 155 `${ CONTENT_BOTTOM_PADDING + newHeight }px`, 156 ); 157 }, 158 }, 159 computed: { 160 files() { 161 return this.$store.getters.sortedFiles; 162 }, 163 prettyDump() { 164 return JSON.stringify(this.dump, null, 2); 165 }, 166 dataLoaded() { 167 return this.files.length > 0; 168 }, 169 activeView() { 170 if (!this.activeDataView && this.files.length > 0) { 171 // eslint-disable-next-line vue/no-side-effects-in-computed-properties 172 this.activeDataView = this.files[0].type; 173 } 174 return this.activeDataView; 175 }, 176 dataViewFiles() { 177 return this.files.filter((f) => this.hasDataView(f)); 178 }, 179 }, 180 watch: { 181 title() { 182 document.title = this.title; 183 }, 184 }, 185 components: { 186 overlay: Overlay, 187 dataview: DataView, 188 datainput: DataInput, 189 dataadb: DataAdb, 190 }, 191}; 192</script> 193<style> 194@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap'); 195 196#app .md-app-container { 197 /* Get rid of tranforms which prevent fixed position from being used */ 198 transform: none!important; 199 min-height: 100vh; 200} 201 202#app .top-toolbar { 203 box-shadow: none; 204 background-color: #fff; 205 background-color: var(--md-theme-default-background, #fff); 206 border-bottom: thin solid rgba(0,0,0,.12); 207 padding: 0 40px; 208} 209 210#app .top-toolbar .md-title { 211 font-family: 'Open Sans', sans-serif; 212 white-space: nowrap; 213 color: #5f6368; 214 margin: 0; 215 padding: 0; 216 font-size: 22px; 217 letter-spacing: 0; 218 font-weight: 600; 219} 220 221.data-view { 222 display: flex; 223 flex-direction: column; 224} 225 226.card-toolbar { 227 border-bottom: 1px solid rgba(0, 0, 0, .12); 228} 229 230.timeline { 231 margin: 16px; 232} 233 234.container { 235 display: flex; 236 flex-wrap: wrap; 237} 238 239.md-button { 240 margin-top: 1em 241} 242 243h1, 244h2 { 245 font-weight: normal; 246} 247 248ul { 249 list-style-type: none; 250 padding: 0; 251} 252 253a { 254 color: #42b983; 255} 256 257.data-inputs { 258 display: flex; 259 flex-wrap: wrap; 260 height: 100%; 261 width: 100%; 262 align-self: center; 263 /* align-items: center; */ 264 align-content: center; 265 justify-content: center; 266} 267 268.data-inputs .input { 269 padding: 15px; 270 flex: 1 1 0; 271 max-width: 840px; 272 /* align-self: center; */ 273} 274 275.data-inputs .input > div { 276 height: 100%; 277} 278 279.data-view-container { 280 padding: 25px 20px 0 20px; 281} 282</style> 283