Skip to content

Commit

Permalink
Add support for on-screen keyboard on mobile devices (#266)
Browse files Browse the repository at this point in the history
Fixes #46

Co-authored-by: djclueless <[email protected]>
  • Loading branch information
mtlynch and djclueless authored Sep 23, 2020
1 parent a770e85 commit a9e45aa
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 0 deletions.
6 changes: 6 additions & 0 deletions app/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ function browserLanguage() {

// Send a keystroke message to the backend, and add a key card to the web UI.
function sendKeystroke(keystroke) {
// On Android, when the user is typing with autocomplete enabled, the browser
// sends dummy keydown events with a keycode of 229. Ignore these events, as
// there's no way to map it to a real key.
if (keystroke.keyCode === 229) {
return;
}
let keyCard = undefined;
if (!keystroke.metaKey) {
keyCard = addKeyCard(keystroke.key);
Expand Down
40 changes: 40 additions & 0 deletions app/templates/custom-elements/remote-screen.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@
:host([fullscreen="true"]) #remote-screen-img.full-height {
height: 100%;
}

#mobile-keyboard-input {
position: fixed;
bottom: -1000px;
}
</style>
<div class="screen-wrapper">
<input id="mobile-keyboard-input" autocapitalize="off" type="text" />
<img id="remote-screen-img" src="/stream?advance_headers=1" />
</div>
<script
Expand Down Expand Up @@ -101,6 +107,40 @@
});

window.addEventListener("resize", this.onWindowResize);

// Detect whether this is a touchscreen device.
let isTouchScreen = false;
this.shadowRoot.addEventListener("touchend", () => {
isTouchScreen = true;
});
this.shadowRoot.addEventListener("click", () => {
if (isTouchScreen) {
this.shadowRoot.getElementById("mobile-keyboard-input").focus();
}
});

// On mobile, the keydown events function differently due to the OS
// attempting to autocomplete text. Instead of listening for keydown
// events, we listen for input events.
const mobileKeyboard = this.shadowRoot.getElementById(
"mobile-keyboard-input"
);
mobileKeyboard.addEventListener("input", (evt) => {
// Handle insertCompositionText, which mean typing in autocomplete
// mode. The global keydown event handler processes all other key
// input events.
if (
evt.inputType === "insertText" ||
evt.inputType === "insertCompositionText"
) {
sendTextInput(evt.data);
}

// Force the autocomplete sequence to restart.
mobileKeyboard.blur();
mobileKeyboard.value = "";
mobileKeyboard.focus();
});
}

disconnectedCallback() {
Expand Down
4 changes: 4 additions & 0 deletions app/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1; scalable=no;"
/>
<meta name="csrf-token" content="{{ csrf_token() }}" />
</head>
<body>
Expand Down

0 comments on commit a9e45aa

Please sign in to comment.