[ermine] Switch to lib.widgets pointer_events_listener

This change removes the local copy of pointer_events_listener and
switches to the lib.widgets improved and shiny pointer_events_listener

Test: All unittests pass and verified that pointer functionality works in ermine
Change-Id: If6db9d1d1148263ff7a911546b9b49159b620b3e
Reviewed-on: https://fuchsia-review.googlesource.com/c/experiences/+/403373
Reviewed-by: David Reveman <reveman@google.com>
Testability-Review: David Reveman <reveman@google.com>
Commit-Queue: Sanjay Chouksey <sanjayc@google.com>
diff --git a/session_shells/ermine/shell/BUILD.gn b/session_shells/ermine/shell/BUILD.gn
index f04854f..c865b97 100644
--- a/session_shells/ermine/shell/BUILD.gn
+++ b/session_shells/ermine/shell/BUILD.gn
@@ -104,6 +104,7 @@
     "//src/experiences/session_shells/ermine/keyboard_shortcuts",
     "//src/experiences/session_shells/ermine/settings",
     "//src/experiences/session_shells/ermine/tiler",
+    "//src/experiences/settings/lib/widgets",
     "//src/sys/component_index/fidl:index",
     "//third_party/dart-pkg/git/flutter/packages/flutter",
     "//third_party/dart-pkg/git/flutter/packages/flutter_localizations",
diff --git a/session_shells/ermine/shell/lib/src/models/app_model.dart b/session_shells/ermine/shell/lib/src/models/app_model.dart
index 4fac667..daf9321 100644
--- a/session_shells/ermine/shell/lib/src/models/app_model.dart
+++ b/session_shells/ermine/shell/lib/src/models/app_model.dart
@@ -13,8 +13,8 @@
 import 'package:fuchsia_services/services.dart' show StartupContext;
 import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'
     show KeyboardShortcuts;
+import 'package:lib.widgets/utils.dart' show PointerEventsListener;
 
-import '../utils/pointer_events_listener.dart';
 import '../utils/presenter.dart';
 import '../utils/suggestions.dart';
 import '../widgets/ask/ask.dart';
diff --git a/session_shells/ermine/shell/lib/src/utils/pointer_events_listener.dart b/session_shells/ermine/shell/lib/src/utils/pointer_events_listener.dart
deleted file mode 100644
index 42527dd..0000000
--- a/session_shells/ermine/shell/lib/src/utils/pointer_events_listener.dart
+++ /dev/null
@@ -1,509 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:developer';
-import 'dart:ui' as ui;
-
-import 'package:fidl_fuchsia_ui_input/fidl_async.dart';
-import 'package:fidl_fuchsia_ui_policy/fidl_async.dart';
-import 'package:fidl_fuchsia_ui_views/fidl_async.dart';
-
-import 'package:flutter/scheduler.dart';
-
-/// The default sampling offset.
-///
-/// Sampling offset is relative to presentation time. If we produce frames
-/// 16.667 ms before presentation and input rate is ~60hz, worst case latency
-/// is 33.334 ms. This however assumes zero latency from the input driver.
-/// 4.666 ms margin is added for this.
-const _defaultSamplingOffset = Duration(milliseconds: -38);
-
-class _Event {
-  PointerEvent p;
-  Flow flow1;
-  Flow flow2;
-  _Event(this.p, this.flow1, this.flow2);
-}
-
-class _DownPointer {
-  _Event last;
-  _Event next;
-  _DownPointer(this.last, this.next);
-}
-
-/// Listens for pointer events through the updated API and injects them into Flutter input pipeline.
-class PointerEventsListener2 extends PointerCaptureListener {
-  // Holds the fidl binding to receive pointer events.
-  final PointerCaptureListenerBinding _pointerCaptureListenerBinding =
-      PointerCaptureListenerBinding();
-
-  PointerEventsListener _pointerEventsListener;
-
-  PointerEventsListener2({
-    SchedulerBinding scheduler,
-    ui.PointerDataPacketCallback callback,
-    Duration samplingOffset,
-  }) {
-    _pointerEventsListener = PointerEventsListener(
-        scheduler: scheduler,
-        callback: callback,
-        samplingOffset: samplingOffset);
-  }
-
-  /// Starts listening to pointer events. Also overrides the original
-  /// [ui.window.onPointerDataPacket] callback to a NOP since we
-  /// inject the pointer events received from the [Scenic] service.
-  void listen(PointerCaptureListenerRegistryProxy registry, ViewRef viewRef) {
-    _pointerEventsListener.setCallbackState();
-
-    registry.registerListener(
-        _pointerCaptureListenerBinding.wrap(this), viewRef);
-  }
-
-  /// |PointerCaptureListener|.
-  @override
-  Future<void> onPointerEvent(PointerEvent event) async {
-    await _pointerEventsListener.onPointerEvent(event);
-  }
-
-  void stop() {
-    _pointerEventsListener.stop();
-    _pointerCaptureListenerBinding.close();
-  }
-}
-
-/// [DEPRECATED] Use PointerEventsListener2 instead.
-/// Listens for pointer events and injects them into Flutter input pipeline.
-class PointerEventsListener extends PointerCaptureListenerHack {
-  // Holds the fidl binding to receive pointer events.
-  final PointerCaptureListenerHackBinding _pointerCaptureListenerBinding =
-      PointerCaptureListenerHackBinding();
-
-  // Holds the last [PointerEvent] mapped to its pointer id. This is used to
-  // determine the correct [PointerDataPacket] to generate at boundary condition
-  // of the screen rect.
-  final _lastPointerEvent = <int, PointerEvent>{};
-
-  // Flag to remember that a down event was seen before a move event.
-  // TODO(sanjayc): Should really convert to a FSM for PointerEvent.
-  final _downEvent = <int, bool>{};
-
-  // Scheduler used for frame callbacks.
-  SchedulerBinding _scheduler;
-
-  // [onPointerDataCallback] used to dispatch pointer data callbacks.
-  ui.PointerDataPacketCallback _callback;
-
-  // Offset used for re-sampling of move events.
-  final Duration _samplingOffset;
-
-  final _queuedEvents = <_Event>[];
-  var _frameCallbackScheduled = false;
-
-  var _sampleTimeNs = 0;
-  final _downPointers = <int, _DownPointer>{};
-
-  PointerEventsListener({
-    SchedulerBinding scheduler,
-    ui.PointerDataPacketCallback callback,
-    Duration samplingOffset,
-  })  : _scheduler = scheduler,
-        _samplingOffset = samplingOffset ?? _defaultSamplingOffset {
-    _callback = callback;
-  }
-
-  /// Starts listening to pointer events. Also overrides the original
-  /// [ui.window.onPointerDataPacket] callback to a NOP since we
-  /// inject the pointer events received from the [Presentation] service.
-  void listen(PresentationProxy presentation) {
-    setCallbackState();
-
-    presentation
-        .capturePointerEventsHack(_pointerCaptureListenerBinding.wrap(this));
-  }
-
-  void setCallbackState() {
-    _scheduler ??= SchedulerBinding.instance;
-    _callback = ui.window.onPointerDataPacket;
-    ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {};
-  }
-
-  /// Stops listening to pointer events. Also restores the
-  /// [ui.window.onPointerDataPacket] callback.
-  void stop() {
-    if (_callback != null) {
-      _cleanupPointerEvents();
-      _pointerCaptureListenerBinding.close();
-
-      // Restore the original pointer events callback on the window.
-      ui.window.onPointerDataPacket = _callback;
-      _callback = null;
-      _lastPointerEvent.clear();
-      _downEvent.clear();
-    }
-  }
-
-  void _cleanupPointerEvents() {
-    for (final lastEvent in _lastPointerEvent.values.toList()) {
-      if (lastEvent.phase != PointerEventPhase.remove &&
-          lastEvent.type != PointerEventType.mouse) {
-        onPointerEvent(_clone(lastEvent, PointerEventPhase.remove, lastEvent.x,
-            lastEvent.y, lastEvent.eventTime));
-      }
-    }
-  }
-
-  PointerEvent _clone(PointerEvent event, PointerEventPhase phase, double x,
-      double y, int eventTime) {
-    return PointerEvent(
-        buttons: event.buttons,
-        deviceId: event.deviceId,
-        eventTime: eventTime,
-        phase: phase,
-        pointerId: event.pointerId,
-        radiusMajor: event.radiusMajor,
-        radiusMinor: event.radiusMinor,
-        type: event.type,
-        x: x,
-        y: y);
-  }
-
-  /// |PointerCaptureListener|.
-  @override
-  Future<void> onPointerEvent(PointerEvent event) async {
-    _onPointerEvent(event);
-  }
-
-  void _onPointerEvent(PointerEvent event) {
-    if (_callback == null) {
-      return;
-    }
-
-    final eventArguments = <String, int>{
-      'eventTimeUs': event.eventTime ~/ 1000,
-    };
-    final flow1 = Flow.begin();
-    Timeline.timeSync('PointerEventsListener.onPointerEvent', () {
-      final flow2 = Flow.begin();
-      Timeline.timeSync('PointerEventsListener.queueEvent', () {
-        final frameTime =
-            _scheduler.currentSystemFrameTimeStamp.inMicroseconds * 1000;
-        // Sanity check event time by clamping to frameTime.
-        final eventTime =
-            event.eventTime < frameTime ? event.eventTime : frameTime;
-        _queuedEvents.add(_Event(
-            _clone(event, event.phase, event.x, event.y, eventTime),
-            flow1,
-            flow2));
-      }, flow: flow2);
-      _dispatchQueuedEvents();
-    }, arguments: eventArguments, flow: flow1);
-  }
-
-  void _dispatchQueuedEvents() {
-    Timeline.timeSync('PointerEventsListener.dispatchQueuedEvents', () {
-      _consumePendingEvents();
-      _updateDownPointers();
-      _dispatchMoveChanges();
-
-      // Schedule frame callback if down pointers exists or events are
-      // still queued. We need the frame callback to determine sample
-      // time. This however makes us produce frames whenever touch points
-      // are present. Probably OK as we'll likely receive an update to
-      // the touch point location each frame that will result in us
-      // actually having to produce a frame.
-      if (_queuedEvents.isNotEmpty || _downPointers.isNotEmpty) {
-        _scheduleFrameCallback();
-      }
-    });
-  }
-
-  void _consumePendingEvents() {
-    final packets = <ui.PointerData>[];
-
-    while (_queuedEvents.isNotEmpty) {
-      final event = _queuedEvents.first;
-
-      // Dispatch non-touch changes directly.
-      var dispatchEvent = true;
-
-      if (event.p.type == PointerEventType.touch) {
-        // Stop consuming touch events if more recent than current sample time.
-        if (event.p.eventTime > _sampleTimeNs) {
-          break;
-        }
-
-        switch (event.p.phase) {
-          case PointerEventPhase.down:
-            _downPointers[event.p.pointerId] = _DownPointer(event, event);
-            break;
-          case PointerEventPhase.move:
-            // Create a _DownPointer event if it does not already exist, i.e.
-            // user is touching screen on boot.
-            if (!_downPointers.containsKey(event.p.pointerId)) {
-              _downPointers[event.p.pointerId] = _DownPointer(event, event);
-            }
-
-            // Move changes will be re-sampled instead of dispatched directly.
-            _downPointers[event.p.pointerId].next = event;
-            dispatchEvent = false;
-            break;
-          case PointerEventPhase.up:
-          case PointerEventPhase.cancel:
-            _downPointers.remove(event.p.pointerId);
-            break;
-          case PointerEventPhase.add:
-          case PointerEventPhase.remove:
-          case PointerEventPhase.hover:
-            break;
-        }
-      }
-
-      if (dispatchEvent) {
-        final eventArguments = <String, int>{
-          'eventTimeUs': event.p.eventTime ~/ 1000,
-        };
-        Timeline.timeSync('PointerEventsListener.consumePendingEvent', () {
-          final packet = _getPacket(event.p);
-          if (packet != null) {
-            packets.add(packet);
-          }
-        }, arguments: eventArguments, flow: Flow.end(event.flow1.id));
-      }
-
-      _queuedEvents.removeAt(0);
-    }
-
-    if (packets.isNotEmpty) {
-      Timeline.timeSync('PointerEventsListener.dispatchPackets', () {
-        _callback(ui.PointerDataPacket(data: packets));
-      });
-    }
-  }
-
-  void _updateDownPointers() {
-    // Update [_downPointers] by examining queued changes.
-    for (var event in _queuedEvents) {
-      final p = _downPointers[event.p.pointerId];
-      if (p != null) {
-        switch (event.p.phase) {
-          case PointerEventPhase.down:
-          case PointerEventPhase.move:
-          case PointerEventPhase.cancel:
-          case PointerEventPhase.up:
-            // Update next event if not already passed sample time.
-            if (p.next.p.eventTime < _sampleTimeNs) {
-              _downPointers[event.p.pointerId].next = event;
-            }
-            break;
-          case PointerEventPhase.add:
-          case PointerEventPhase.remove:
-          case PointerEventPhase.hover:
-            break;
-        }
-      }
-    }
-  }
-
-  void _dispatchMoveChanges() {
-    final packets = <ui.PointerData>[];
-
-    // Add move changes for [_downPointers].
-    for (var v in _downPointers.values) {
-      var x = v.next.p.x;
-      var y = v.next.p.y;
-      var eventTime = v.next.p.eventTime;
-      // Re-sample if next time stamp is past sample time.
-      if (v.next.p.eventTime > _sampleTimeNs &&
-          v.next.p.eventTime > v.last.p.eventTime) {
-        var resampleArguments = <String, int>{
-          'lastEventTimeUs': v.last.p.eventTime ~/ 1000,
-          'nextEventTimeUs': v.next.p.eventTime ~/ 1000
-        };
-        Timeline.timeSync('PointerEventsListener.resampledEvent', () {
-          final interval = (v.next.p.eventTime - v.last.p.eventTime).toDouble();
-          final scalar =
-              (_sampleTimeNs - v.last.p.eventTime).toDouble() / interval;
-          x = v.last.p.x + (v.next.p.x - v.last.p.x) * scalar;
-          y = v.last.p.y + (v.next.p.y - v.last.p.y) * scalar;
-          eventTime = _sampleTimeNs;
-        }, arguments: resampleArguments);
-      }
-
-      // Add move change if time stamp is greater than last event.
-      if (eventTime > v.last.p.eventTime) {
-        final event = _clone(v.next.p, PointerEventPhase.move, x, y, eventTime);
-        final eventArguments = <String, int>{
-          'eventTimeUs': eventTime ~/ 1000,
-        };
-        Timeline.timeSync('PointerEventsListener.addMoveEvent', () {
-          final packet = _getPacket(event);
-          if (packet != null) {
-            packets.add(packet);
-          }
-        }, arguments: eventArguments, flow: Flow.end(v.last.flow2.id));
-
-        Timeline.timeSync('PointerEventsListener.updateLastEvent', () {
-          v.last.p = event;
-          v.last.flow2 = v.next.flow2;
-        }, flow: Flow.end(v.next.flow1.id));
-      }
-    }
-
-    if (packets.isNotEmpty) {
-      Timeline.timeSync('PointerEventsListener.dispatchPackets', () {
-        _callback(ui.PointerDataPacket(data: packets));
-      });
-    }
-  }
-
-  void _scheduleFrameCallback() {
-    if (_frameCallbackScheduled) {
-      return;
-    }
-    _frameCallbackScheduled = true;
-    _scheduler.scheduleFrameCallback((_) {
-      _frameCallbackScheduled = false;
-      _sampleTimeNs = (_scheduler.currentSystemFrameTimeStamp + _samplingOffset)
-              .inMicroseconds *
-          1000;
-      var frameArguments = <String, int>{
-        'frameTimeUs': _scheduler.currentSystemFrameTimeStamp.inMicroseconds,
-      };
-      if (_queuedEvents.isNotEmpty) {
-        final lastEventTime = _queuedEvents.last.p.eventTime;
-        frameArguments['lastEventTimeUs'] = lastEventTime ~/ 1000;
-        frameArguments['eventTimeMarginUs'] =
-            (lastEventTime - _sampleTimeNs) ~/ 1000;
-      }
-      Timeline.timeSync(
-        'PointerEventsListener.onFrameCallback',
-        _dispatchQueuedEvents,
-        arguments: frameArguments,
-      );
-    });
-    Timeline.timeSync('PointerEventsListener.scheduleFrameCallback', () {
-      _scheduler.scheduleFrame();
-    });
-  }
-
-  ui.PointerChange _changeFromPointerEvent(PointerEvent event) {
-    final lastEvent = _lastPointerEvent[event.pointerId] ?? event;
-
-    switch (event.phase) {
-      case PointerEventPhase.add:
-        return ui.PointerChange.add;
-      case PointerEventPhase.hover:
-        return ui.PointerChange.hover;
-      case PointerEventPhase.down:
-        _downEvent[event.pointerId] = true;
-        return ui.PointerChange.down;
-      case PointerEventPhase.move:
-        // If move is the first event, convert to `add` event. Otherwise,
-        // flutter pointer state machine throws an exception.
-        if (event.type != PointerEventType.mouse &&
-            _lastPointerEvent[event.pointerId] == null) {
-          return ui.PointerChange.add;
-        }
-
-        // If move event was seen before down event, convert to `down` event.
-        if (event.type != PointerEventType.mouse &&
-            _downEvent[event.pointerId] != true) {
-          _downEvent[event.pointerId] = true;
-          return ui.PointerChange.down;
-        }
-
-        // For mouse, return a hover event if no buttons were pressed.
-        if (event.type == PointerEventType.mouse && event.buttons == 0) {
-          return ui.PointerChange.hover;
-        }
-
-        // Check if this is a boundary condition and convert to up/down event.
-        if (lastEvent?.phase == PointerEventPhase.move) {
-          if (_outside(lastEvent) && _inside(event)) {
-            return ui.PointerChange.down;
-          }
-          if (_inside(lastEvent) && _outside(event)) {
-            return ui.PointerChange.cancel;
-          }
-        }
-
-        return ui.PointerChange.move;
-      case PointerEventPhase.up:
-        _downEvent[event.pointerId] = false;
-        return ui.PointerChange.up;
-      case PointerEventPhase.remove:
-        return ui.PointerChange.remove;
-      case PointerEventPhase.cancel:
-      default:
-        return ui.PointerChange.cancel;
-    }
-  }
-
-  ui.PointerDeviceKind _kindFromPointerEvent(PointerEvent event) {
-    switch (event.type) {
-      case PointerEventType.mouse:
-        return ui.PointerDeviceKind.mouse;
-      case PointerEventType.stylus:
-        return ui.PointerDeviceKind.stylus;
-      case PointerEventType.invertedStylus:
-        return ui.PointerDeviceKind.invertedStylus;
-      case PointerEventType.touch:
-      default:
-        return ui.PointerDeviceKind.touch;
-    }
-  }
-
-  ui.PointerData _getPacket(PointerEvent event) {
-    final lastEvent = _lastPointerEvent[event.pointerId];
-
-    // Only allow add and remove pointer events from outside the window bounds.
-    // For other events, we drop them if the last two were outside the window
-    // bounds. If any of current event or last event lies inside the window,
-    // we generate a synthetic down or up event.
-    if (event.phase != PointerEventPhase.add &&
-        event.phase != PointerEventPhase.remove &&
-        _outside(event) &&
-        _outside(lastEvent ?? event)) {
-      _lastPointerEvent[event.pointerId] = event;
-      return null;
-    }
-
-    // Calculate the offset between two events.
-    final delta = lastEvent != null
-        ? ui.Offset(event.x - lastEvent.x, event.y - lastEvent.y)
-        : ui.Offset(0, 0);
-
-    // Convert from PointerEvent to PointerData.
-    final data = ui.PointerData(
-      buttons: event.buttons,
-      device: event.pointerId,
-      timeStamp: Duration(microseconds: event.eventTime ~/ 1000),
-      change: _changeFromPointerEvent(event),
-      kind: _kindFromPointerEvent(event),
-      physicalDeltaX: delta.dx * ui.window.devicePixelRatio,
-      physicalDeltaY: delta.dy * ui.window.devicePixelRatio,
-      physicalX: event.x * ui.window.devicePixelRatio,
-      physicalY: event.y * ui.window.devicePixelRatio,
-      pointerIdentifier: event.pointerId,
-      synthesized: false,
-    );
-
-    // Remember this event for checking boundary condition on the next event.
-    _lastPointerEvent[event.pointerId] = event;
-
-    return data;
-  }
-
-  bool _inside(PointerEvent event) {
-    return event != null &&
-        event.x * ui.window.devicePixelRatio >= 0 &&
-        event.x * ui.window.devicePixelRatio < ui.window.physicalSize.width &&
-        event.y * ui.window.devicePixelRatio >= 0 &&
-        event.y * ui.window.devicePixelRatio < ui.window.physicalSize.height;
-  }
-
-  bool _outside(PointerEvent event) => !_inside(event);
-}
diff --git a/session_shells/ermine/shell/test/app_model_test.dart b/session_shells/ermine/shell/test/app_model_test.dart
index b95f8f8..fed816b 100644
--- a/session_shells/ermine/shell/test/app_model_test.dart
+++ b/session_shells/ermine/shell/test/app_model_test.dart
@@ -5,6 +5,7 @@
 import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'
     show KeyboardShortcuts;
 import 'package:fuchsia_internationalization_flutter/internationalization.dart';
+import 'package:lib.widgets/utils.dart' show PointerEventsListener;
 import 'package:test/test.dart';
 import 'package:mockito/mockito.dart';
 
@@ -12,7 +13,6 @@
 import 'package:ermine/src/models/cluster_model.dart';
 import 'package:ermine/src/models/ermine_story.dart';
 import 'package:ermine/src/models/status_model.dart';
-import 'package:ermine/src/utils/pointer_events_listener.dart';
 import 'package:ermine/src/utils/suggestions.dart';
 import 'package:ermine/src/models/app_model.dart';