Implement long touch to select text

trigger selection mode(actually touch->mouse) by contextmenu(long press).

Bug: 375326606
Test: long press to select
Change-Id: Ib9ade088825d727a69f4bc3d94507946c12a53e5
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 1442d30..bfa425d 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -300,6 +300,8 @@
                                                     .setVisibility(View.VISIBLE);
                                             mBootCompleted.open();
                                             updateModifierKeysVisibility();
+                                            mWebView.evaluateJavascript(
+                                                    TerminalView.TOUCH_TO_MOUSE_HANDLER, null);
                                         }
                                     }
                                 });
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
index 77a48be..3f09e35 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
@@ -79,6 +79,73 @@
 """;
     public static final String ENABLE_CTRL_KEY = "(function(){window.ctrl=true;})();";
 
+    // TODO(b/375326606): consider contribution on
+    // upstream(https://github.com/xtermjs/xterm.js/issues/3727)
+    public static final String TOUCH_TO_MOUSE_HANDLER =
+            """
+(function() {
+let convertTouchToMouse = false;
+function touchHandler(event) {
+  const contextmenuByTouch =
+      event.type === 'contextmenu' && event.pointerType === 'touch';
+  // Only proceed for long touches (contextmenu) or when converting touch to
+  // mouse
+  if (!contextmenuByTouch && !convertTouchToMouse) {
+    return;
+  }
+
+  const touch = event.changedTouches ? event.changedTouches[0] : event;
+
+  let type;
+  switch (event.type) {
+    case 'contextmenu':
+      convertTouchToMouse = true;
+      type = 'mousedown';
+      break;
+    case 'touchmove':
+      type = 'mousemove';
+      break;
+    case 'touchend':
+      convertTouchToMouse = false;
+      type = 'mouseup';
+      break;
+    default:
+      convertTouchToMouse = false;
+      return;
+  }
+
+  const simulatedEvent = new MouseEvent(type, {
+    bubbles: true,
+    cancelable: true,
+    view: window,
+    detail: 1,
+    screenX: touch.screenX,
+    screenY: touch.screenY,
+    clientX: touch.clientX,
+    clientY: touch.clientY,
+    button: 0,  // left click
+  });
+
+  touch.target.dispatchEvent(simulatedEvent);
+
+  // Prevent default behavior for touch events (except contextmenu)
+  if (event.type !== 'contextmenu') {
+    event.preventDefault();
+    event.stopPropagation();
+  }
+}
+const eventOptions = {
+  capture: true,
+  passive: false
+};
+document.addEventListener('touchstart', touchHandler, eventOptions);
+document.addEventListener('touchmove', touchHandler, eventOptions);
+document.addEventListener('touchend', touchHandler, eventOptions);
+document.addEventListener('touchcancel', touchHandler, eventOptions);
+document.addEventListener('contextmenu', touchHandler, eventOptions);
+})();
+""";
+
     private final AccessibilityManager mA11yManager;
 
     public TerminalView(Context context, AttributeSet attrs) {