Merge timelines together when timeline is collapsed

Test: N/A
Change-Id: I48b5bbd45265681e0c515fa0138de4c6711f79f7
diff --git a/tools/winscope/src/BottomNavigation.vue b/tools/winscope/src/BottomNavigation.vue
index 5f30437..52e4c48 100644
--- a/tools/winscope/src/BottomNavigation.vue
+++ b/tools/winscope/src/BottomNavigation.vue
@@ -26,15 +26,11 @@
                 <b>Seek time</b>: {{ seekTime }}
               </div>
               <div class="active-timeline" v-show="minimized">
-                <md-icon>
-                  {{timelineActiveFile.type.icon}}
-                  <md-tooltip md-direction="right">{{timelineActiveFile.type.name}}</md-tooltip>
-                </md-icon>
                 <timeline
-                  :items="timelineActiveFile.timeline"
-                  :selected-index="timelineActiveFile.selectedIndex"
+                  :items="mergedTimeline.timeline"
+                  :selected-index="mergedTimeline.selectedIndex"
                   :scale="scale"
-                  @item-selected="onTimelineItemSelected($event, timelineActiveFileIndex)"
+                  @item-selected="onMergedTimelineItemSelected($event)"
                   class="timeline"
                 />
               </div>
@@ -107,49 +103,6 @@
     expanded() {
       return !this.minimized;
     },
-    timelineActiveFile() {
-      if (this.activeFile) {
-        return this.activeFile;
-      }
-
-      if (!this.dataViewPositions) {
-        return this.files[0];
-      }
-
-      // If not active file is selected figure out which one takes up the most
-      // of the screen and mark that one as the active file
-      const visibleHeight =
-        Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
-      let maxScreenSpace = 0;
-      let selectedFile = this.files[0];
-      for (const file of this.files) {
-        const pos = this.dataViewPositions[file.filename];
-
-        let screenSpace = 0;
-        if (0 <= pos.top && pos.top <= visibleHeight) {
-          screenSpace = Math.min(visibleHeight, pos.bottom) - pos.top;
-        } else if (0 <= pos.bottom && pos.bottom <= visibleHeight) {
-          screenSpace = pos.bottom - Math.max(0, pos.top);
-        } else if (pos.top <=0 && pos.bottom >= visibleHeight) {
-          screenSpace = visibleHeight;
-        }
-
-        if (screenSpace >= maxScreenSpace) {
-          maxScreenSpace = screenSpace;
-          selectedFile = file;
-        }
-      }
-
-      return selectedFile;
-    },
-    timelineActiveFileIndex() {
-      for (let i = 0; i < this.files.length; i++) {
-        if (this.files[i].filename == this.timelineActiveFile.filename) {
-          return i;
-        }
-      }
-      throw "Active file index not found";
-    },
     seekTime() {
       return nanos_to_string(this.currentTimestamp);
     },
@@ -158,6 +111,50 @@
       var mi = Math.min(...(this.files.map(f => Math.min(...f.timeline))));
       return [mi, mx];
     },
+    mergedTimeline() {
+      const mergedTimeline = {
+        timeline: [], // Array of integers timestamps
+        selectedIndex: 0,
+      };
+
+      const timelineIndexes = [];
+      const timelines = [];
+      for (const file of this.files) {
+        timelineIndexes.push(0);
+        timelines.push(file.timeline);
+      }
+
+      while(true) {
+        let minTime = Infinity;
+        let timelineToAdvance;
+
+        for (let i = 0; i < timelines.length; i++) {
+          const timeline = timelines[i];
+          const index = timelineIndexes[i];
+
+          if (index >= timeline.length) {
+            continue;
+          }
+
+          const time = timeline[index];
+
+          if (time < minTime) {
+            minTime = time;
+            timelineToAdvance = i;
+          }
+        }
+
+        if (timelineToAdvance === undefined) {
+          // No more elements left
+          break;
+        }
+
+        timelineIndexes[timelineToAdvance]++;
+        mergedTimeline.timeline.push(minTime);
+      }
+
+      return mergedTimeline;
+    }
   },
   methods: {
     toggle() {
@@ -166,19 +163,40 @@
     fileIsVisible(f) {
       return this.visibleDataViews.includes(f.filename);
     },
+    updateSelectedIndex(file, timestamp) {
+      file.selectedIndex = findLastMatchingSorted(
+        file.timeline,
+        function(array, idx) {
+          return parseInt(array[idx]) <= timestamp;
+        }
+      );
+    },
+    onMergedTimelineItemSelected(index) {
+      this.mergedTimeline.selectedIndex = index;
+      const timestamp = this.mergedTimeline.timeline[index];
+      this.files.forEach(file => this.updateSelectedIndex(file, timestamp));
+      this.currentTimestamp = timestamp;
+    },
     onTimelineItemSelected(index, timelineIndex) {
       this.files[timelineIndex].selectedIndex = index;
-      var t = parseInt(this.files[timelineIndex].timeline[index]);
-      for (var i = 0; i < this.files.length; i++) {
+      const timestamp = parseInt(this.files[timelineIndex].timeline[index]);
+      for (let i = 0; i < this.files.length; i++) {
         if (i != timelineIndex) {
-          this.files[i].selectedIndex = findLastMatchingSorted(this.files[i].timeline, function(array, idx) {
-            return parseInt(array[idx]) <= t;
-          });
+          this.updateSelectedIndex(this.files[i], timestamp);
         }
       }
-      this.currentTimestamp = t;
+
+      this.updateSelectedIndex(this.mergedTimeline, timestamp);
+
+      this.currentTimestamp = timestamp;
     },
     advanceTimeline(direction) {
+      if (0 < this.mergedTimeline.selectedIndex + direction &&
+            this.mergedTimeline.selectedIndex + direction <
+              this.mergedTimeline.timeline.length) {
+        this.mergedTimeline.selectedIndex += direction;
+      }
+
       var closestTimeline = -1;
       var timeDiff = Infinity;
       for (var idx = 0; idx < this.files.length; idx++) {