Sync with video when trimming timeline

Test: visual
Change-Id: Ibfe2a5d6ce5bbfbb900b22344723b43c4d268d43
diff --git a/tools/winscope/src/Overlay.vue b/tools/winscope/src/Overlay.vue
index 5b9f805..09c8c8a 100644
--- a/tools/winscope/src/Overlay.vue
+++ b/tools/winscope/src/Overlay.vue
@@ -204,6 +204,8 @@
                     :end-timestamp="0"
                     :scale="scale"
                     v-on:crop="onTimelineCrop"
+                    v-on:showVideoAt="changeVideoTimestamp"
+                    v-on:resetVideoTimestamp="resetVideoTimestamp"
                   />
                 </div>
 
@@ -556,6 +558,12 @@
     onTimelineCrop(cropDetails) {
       this.crop = cropDetails;
     },
+    changeVideoTimestamp(ts) {
+      this.$refs.video.selectFrameAtTime(ts);
+    },
+    resetVideoTimestamp() {
+      this.$refs.video.jumpToSelectedIndex();
+    }
   },
   components: {
     'timeline': Timeline,
diff --git a/tools/winscope/src/TimelineSelection.vue b/tools/winscope/src/TimelineSelection.vue
index 8595ed9..960af85 100644
--- a/tools/winscope/src/TimelineSelection.vue
+++ b/tools/winscope/src/TimelineSelection.vue
@@ -100,12 +100,25 @@
       return this.translate(item);
     },
     translate(cx) {
-      var scale = [...this.scale];
+      const scale = [...this.scale];
       if (scale[0] >= scale[1]) {
         return cx;
       }
 
-      return (((cx - scale[0]) / (scale[1] - scale[0])) * 100 - (1 /*pointWidth*/)) + "%";
+      return (((cx - scale[0]) / (scale[1] - scale[0])) * (100 - (1 /*pointWidth*/))) + "%";
+    },
+    getPositionAsTimestamp(position) {
+      const scale = [...this.scale];
+      if (scale[0] >= scale[1]) {
+        return position;
+      }
+
+      const width = this.$refs.timelineSvg.clientWidth;
+      const pointWidth = 1/100 * width;
+      const positionAsPercent = position / (width - pointWidth);
+
+      return scale[0] +
+        positionAsPercent * (scale[1] - scale[0]);
     },
     emitCropDetails() {
       const width = this.$refs.timelineSvg.clientWidth;
@@ -144,6 +157,8 @@
             } else {
               this.selectionEndPosition = this.$refs.timelineSvg.clientWidth;
             }
+
+            this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionEndPosition));
           } else {
             this.selectionEndPosition = this.selectionStartX;
 
@@ -153,6 +168,8 @@
             } else {
               this.selectionStartPosition = 0;
             }
+
+            this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionStartPosition));
           }
         }
       }
@@ -160,6 +177,7 @@
       this.createSelectionMouseUpEventListener = e => {
         this.selecting = false;
         document.body.style.cursor = null;
+        this.$emit('resetVideoTimestamp');
       };
 
       this.$refs.timelineSvg
@@ -242,6 +260,7 @@
         this.resizeStartPos = this.selectionStartPosition;
 
         document.body.style.cursor = "ew-resize";
+        this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionStartPosition));
       };
 
       this.rightResizeDraggerMouseDownEventListener = e => {
@@ -251,6 +270,7 @@
         this.resizeEndPos = this.selectionEndPosition;
 
         document.body.style.cursor = "ew-resize";
+        this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionEndPosition));
       };
 
       this.resizeMouseMoveEventListener = e => {
@@ -265,6 +285,8 @@
           }
 
           this.selectionStartPosition = newStartPos;
+
+          this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionStartPosition));
         }
 
         if (this.resizeingRight) {
@@ -278,6 +300,7 @@
           }
 
           this.selectionEndPosition = newEndPos;
+          this.$emit('showVideoAt', this.getPositionAsTimestamp(this.selectionEndPosition));
         }
       };
 
@@ -285,6 +308,7 @@
         this.resizeingLeft = false;
         this.resizeingRight = false;
         document.body.style.cursor = null;
+        this.$emit('resetVideoTimestamp');
       }
 
       document.body.style.cursor = null;
diff --git a/tools/winscope/src/VideoView.vue b/tools/winscope/src/VideoView.vue
index 16d08da..4180767 100644
--- a/tools/winscope/src/VideoView.vue
+++ b/tools/winscope/src/VideoView.vue
@@ -21,15 +21,15 @@
   />
 </template>
 <script>
-const EPSILON = 0.00001
+const EPSILON = 0.00001;
 
 function uint8ToString(array) {
-  var chunk = 0x8000;
-  var out = [];
-  for (var i = 0; i < array.length; i += chunk) {
+  const chunk = 0x8000;
+  const out = [];
+  for (let i = 0; i < array.length; i += chunk) {
     out.push(String.fromCharCode.apply(null, array.subarray(i, i + chunk)));
   }
-  return out.join("");
+  return out.join('');
 }
 
 export default {
@@ -48,31 +48,37 @@
       } else {
         return `height: ${this.height}`;
       }
-    }
+    },
   },
   methods: {
     arrowUp() {
-      return true
+      return true;
     },
     arrowDown() {
       return true;
     },
-    selectFrame(idx) {
-      var time = (this.file.timeline[idx] - this.file.timeline[0]) / 1000000000 + EPSILON;
+    selectFrameAtTime(timestamp) {
+      const time = (timestamp - this.file.timeline[0]) / 1000000000 + EPSILON;
       this.$refs.video.currentTime = time;
     },
+    selectFrame(idx) {
+      this.selectFrameAtTime(this.file.timeline[idx]);
+    },
+    jumpToSelectedIndex() {
+      this.selectFrame(this.file.selectedIndex);
+    },
   },
   watch: {
     selectedIndex() {
       this.selectFrame(this.file.selectedIndex);
-    }
+    },
   },
   mounted() {
     this.$el.addEventListener('canplay', (e) => {
       this.$emit('loaded');
     });
   },
-}
+};
 
 </script>
 <style>