feat(player): add hardware keyboard support

This commit introduces hardware keyboard support for the video player across all platforms (iOS, Android, and Web).

- Utilizes the `expo-key-event` native module to capture physical keyboard events.
- Maps the Spacebar to toggle play/pause.
- Maps the Left and Right Arrow keys to skip backward and forward.

Co-authored-by: Darkatek7 <50767771+Darkatek7@users.noreply.github.com>
This commit is contained in:
Péter Tombor
2026-06-26 18:21:28 +02:00
parent 9e29305e28
commit ff708ec832
3 changed files with 22 additions and 1 deletions

View File

@@ -38,6 +38,7 @@
"expo-font": "~56.0.6",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.11",
"expo-key-event": "^1.9.0",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.14",
"expo-localization": "~56.0.6",
@@ -976,6 +977,8 @@
"expo-keep-awake": ["expo-keep-awake@56.0.3", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-CLMJXtEiMKknD3Rpm8CRwE6ZJUzu2yCEmRk1sgfHAJ1zIbuEWY3dpPDubtsnuzWm+2k6Sru+yaFbYsvPWmTiBA=="],
"expo-key-event": ["expo-key-event@1.9.0", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-ZV/+iAz/0WeACFG4IR+KOTtm/7PTIsl+GaD/PYBM89PhvueQ6zYuvjPb5A47sWX2WjDunzVN0R1rOd/HIjA8aA=="],
"expo-linear-gradient": ["expo-linear-gradient@56.0.4", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-KUp1dNSRtuMyiExhf6FJf5YUtmw2cRaPytl10HQi7isj5Yac38udmD55T2tglNYTZlvgT5+oflpyFoH15hmOcw=="],
"expo-linking": ["expo-linking@56.0.14", "", { "dependencies": { "expo-constants": "~56.0.18", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-IvVQHWC+Cj4fK5qD3iEVYqpU2a4rLW0IpAAlGJ4MH+H1fyZiHh3eN6qg2WmoclOEPfYATSuEa+dQT6wfgVpXlQ=="],

View File

@@ -3,9 +3,10 @@ import type {
BaseItemDto,
MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client";
import { useKeyEventListener } from "expo-key-event";
import { useLocalSearchParams } from "expo-router";
import { type FC, useCallback, useEffect, useState } from "react";
import { StyleSheet, useWindowDimensions, View } from "react-native";
import { Platform, StyleSheet, useWindowDimensions, View } from "react-native";
import Animated, {
Easing,
type SharedValue,
@@ -205,6 +206,22 @@ export const Controls: FC<Props> = ({
play,
});
useKeyEventListener((e) => {
if (episodeView || showAudioSlider) return;
if (e?.eventType !== "press") return;
const key = e.key;
if (key === " " || key === "Spacebar" || key === "Space") {
togglePlay();
} else if (!Platform.isTV && key === "ArrowLeft") {
// Exclude TV platforms to prevent conflicts with the remote control,
// which uses the same arrow keys for directional UI navigation.
handleSkipBackward();
} else if (!Platform.isTV && key === "ArrowRight") {
handleSkipForward();
}
});
// Time management hook
const { currentTime, remainingTime } = useVideoTime({
progress,

View File

@@ -61,6 +61,7 @@
"expo-font": "~56.0.6",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.11",
"expo-key-event": "^1.9.0",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.14",
"expo-localization": "~56.0.6",