Fix to Android color theme

This commit is contained in:
WardPearce
2026-02-18 07:13:43 +13:00
parent 5c5d103312
commit 6f6885404a
3 changed files with 95 additions and 114 deletions
@@ -3,7 +3,7 @@
import { getBestThumbnail } from '$lib/images';
import { videoLength } from '$lib/numbers';
import { generateChapterWebVTT, type ParsedDescription } from '$lib/description';
import { Capacitor } from '@capacitor/core';
import { Capacitor, SystemBars, SystemBarsStyle, SystemBarType } from '@capacitor/core';
import { error, type Page } from '@sveltejs/kit';
import Mousetrap from 'mousetrap';
import { CapacitorMusicControls } from 'capacitor-music-controls-plugin';
@@ -20,6 +20,8 @@
invidiousInstanceStore,
isAndroidTvStore,
playerAlwaysLoopStore,
playerAndroidLockOrientation,
playerAndroidPauseOnNetworkChange,
playerAutoPlayStore,
playerCCByDefault,
playerDefaultLanguage,
@@ -59,9 +61,10 @@
import Airplay from './settings/Airplay.svelte';
import Pip from './settings/Pip.svelte';
import FullscreenToggle from './settings/FullscreenToggle.svelte';
import { AndroidPlayer } from '$lib/player/android';
import Timeline from './Timeline.svelte';
import TouchControls from './TouchControls.svelte';
import { Network, type ConnectionStatus } from '@capacitor/network';
import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
interface Props {
data: { video: VideoPlay; content: ParsedDescription; playlistId: string | null };
@@ -86,11 +89,12 @@
let watchProgressInterval: ReturnType<typeof setInterval>;
let showVideoRetry = $state(false);
let androidPlayer: AndroidPlayer;
let player: shaka.Player;
let sabrAdapter: SabrStreamingAdapter | null;
let androidInitialNetworkStatus: ConnectionStatus | undefined;
let androidOriginalOrigination: ScreenOrientationResult | undefined;
let playerContainer: HTMLElement;
let playerCurrentPlaybackState = $state(false);
let playerMaxKnownTime = $state(data.video.lengthSeconds);
@@ -369,6 +373,58 @@
}, 5000);
}
async function onAndroidFullscreenChange() {
const videoFormats = data.video.adaptiveFormats.filter((format) =>
format.type.startsWith('video/')
);
const isFullScreen = !!document.fullscreenElement;
if (isFullScreen) {
// Ensure bar color is black while in fullscreen
await SystemBars.setStyle({ style: SystemBarsStyle.Light });
await SystemBars.hide({
bar: SystemBarType.NavigationBar
});
await SystemBars.hide({
bar: SystemBarType.StatusBar
});
} else {
await setStatusBarColor();
}
if (!$playerAndroidLockOrientation) return;
if (isFullScreen && videoFormats[0].resolution) {
const widthHeight = videoFormats[0].resolution.split('x');
if (widthHeight.length !== 2) return;
if (Number(widthHeight[0]) > Number(widthHeight[1])) {
await ScreenOrientation.lock({ orientation: 'landscape' });
} else {
await ScreenOrientation.lock({ orientation: 'portrait' });
}
} else {
await ScreenOrientation.lock({
orientation: (androidOriginalOrigination as ScreenOrientationResult).type
});
}
}
async function androidHandleRotate() {
if (
Capacitor.getPlatform() !== 'android' ||
data.video.adaptiveFormats.length === 0 ||
$isAndroidTvStore
)
return;
androidOriginalOrigination = await ScreenOrientation.orientation();
document.addEventListener('fullscreenchange', onAndroidFullscreenChange);
}
onMount(async () => {
shaka.polyfill.installAll();
if (!shaka.Player.isBrowserSupported()) {
@@ -417,8 +473,6 @@
window.addEventListener('resize', updateVideoPlayerHeight);
updateVideoPlayerHeight();
androidPlayer = new AndroidPlayer(data.video, playerElement);
await player.attach(playerElement);
player?.addEventListener('error', (event) => {
@@ -454,6 +508,19 @@
playerElement?.addEventListener('volumechange', saveVolumePreference);
if (Capacitor.getPlatform() === 'android') {
await androidHandleRotate();
androidInitialNetworkStatus = await Network.getStatus();
Network.addListener('networkStatusChange', (networkStatus) => {
if (
androidInitialNetworkStatus?.connectionType !== networkStatus.connectionType &&
$playerAndroidPauseOnNetworkChange
) {
playerElement?.pause();
}
});
await CapacitorMusicControls.create({
track: data.video.title,
artist: data.video.author,
@@ -778,7 +845,16 @@
if (Capacitor.getPlatform() === 'android') {
await setStatusBarColor();
await CapacitorMusicControls.destroy();
await androidPlayer.destroy();
if ($isAndroidTvStore) {
document.removeEventListener('fullscreenchange', onAndroidFullscreenChange);
}
if (androidOriginalOrigination) {
await ScreenOrientation.lock({
orientation: androidOriginalOrigination.type
});
}
}
try {
-95
View File
@@ -1,95 +0,0 @@
import type { VideoPlay } from '$lib/api/model';
import { playerAndroidLockOrientation, playerAndroidPauseOnNetworkChange } from '$lib/store';
import { setStatusBarColor } from '$lib/theme';
import { get } from 'svelte/store';
import { Capacitor, SystemBars, SystemBarsStyle, SystemBarType } from '@capacitor/core';
import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
import { Network, type ConnectionStatus } from '@capacitor/network';
export class AndroidPlayer {
video: VideoPlay;
originalOrigination: ScreenOrientationResult | undefined;
initialNetworkStatus: ConnectionStatus | undefined;
playerElement: HTMLMediaElement | undefined;
constructor(video: VideoPlay, playerElement: HTMLMediaElement | undefined) {
this.video = video;
this.playerElement = playerElement;
if (Capacitor.getPlatform() !== 'android') return;
ScreenOrientation.orientation().then((originalOrigination) => {
this.originalOrigination = originalOrigination;
});
Network.getStatus().then((initialNetworkStatus) => {
this.initialNetworkStatus = initialNetworkStatus;
});
document.addEventListener('fullscreenchange', this.onFullscreenChange);
Network.addListener('networkStatusChange', (networkStatus) => {
if (
this.initialNetworkStatus?.connectionType !== networkStatus.connectionType &&
get(playerAndroidPauseOnNetworkChange)
) {
this.playerElement?.pause();
}
});
}
async destroy() {
if (Capacitor.getPlatform() !== 'android') return;
document.removeEventListener('fullscreenchange', this.onFullscreenChange);
if (this.originalOrigination) {
await ScreenOrientation.lock({
orientation: this.originalOrigination.type
});
}
await Network.removeAllListeners();
}
async onFullscreenChange() {
if (Capacitor.getPlatform() !== 'android') return;
const videoFormats = this.video.adaptiveFormats.filter((format) =>
format.type.startsWith('video/')
);
const isFullScreen = !!document.fullscreenElement;
if (isFullScreen) {
// Ensure bar color is black while in fullscreen
await SystemBars.setStyle({ style: SystemBarsStyle.Light });
await SystemBars.hide({
bar: SystemBarType.NavigationBar
});
await SystemBars.hide({
bar: SystemBarType.StatusBar
});
} else {
await setStatusBarColor();
}
if (!get(playerAndroidLockOrientation)) return;
if (isFullScreen && videoFormats[0].resolution) {
const widthHeight = videoFormats[0].resolution.split('x');
if (widthHeight.length !== 2) return;
if (Number(widthHeight[0]) > Number(widthHeight[1])) {
await ScreenOrientation.lock({ orientation: 'landscape' });
} else {
await ScreenOrientation.lock({ orientation: 'portrait' });
}
} else {
if (this.originalOrigination)
await ScreenOrientation.lock({
orientation: this.originalOrigination.type
});
}
}
}
+12 -12
View File
@@ -81,20 +81,20 @@
});
onMount(async () => {
ui();
if (Capacitor.getPlatform() === 'android' && !$themeColorStore) {
try {
const colorPalette = await colorTheme.getColorPalette();
const colorAsHex = convertToHexColorCode(colorPalette.primary);
themeColorStore.set(colorAsHex);
await ui('theme', colorAsHex);
} catch {
// Continue regardless of error
}
}
await ui();
if (Capacitor.getPlatform() === 'android') {
if (!$themeColorStore) {
try {
const colorPalette = await colorTheme.getColorPalette();
const colorAsHex = convertToHexColorCode(colorPalette.primary);
themeColorStore.set(colorAsHex);
await ui('theme', colorAsHex);
} catch {
// Continue regardless of error
}
}
document.addEventListener('click', async (event: MouseEvent) => {
// Handles opening links in browser for android.
const link = (event.target as HTMLElement).closest('a');