'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; type PermissionState = 'granted' | 'denied' | 'prompt'; type BackgroundModePlugin = { checkMicrophonePermission: () => Promise<{ microphone: PermissionState | string }>; requestMicrophonePermission: () => Promise<{ microphone: PermissionState | string }>; checkNotificationsPermission: () => Promise<{ notifications: PermissionState | string }>; requestNotificationsPermission: () => Promise<{ notifications: PermissionState | string }>; checkBatteryOptimizations: () => Promise<{ enabled: boolean }>; requestDisableBatteryOptimizations: () => Promise; disableWebViewOptimizations: () => Promise; enable: (options: { title: string; text: string; channelName: string; channelDescription: string; resume: boolean; silent: boolean; disableWebViewOptimization: boolean; }) => Promise; addListener: (eventName: string, listener: () => void) => Promise; }; type CapacitorRuntime = { Plugins?: { BackgroundMode?: BackgroundModePlugin; }; }; type CapacitorWindow = Window & { Capacitor?: CapacitorRuntime; }; const STREAM_URL = 'https://kryz.out.airtime.pro/kryz_b'; export default function HomePage() { const audioRef = useRef(null); const [status, setStatus] = useState('Stream is stopped.'); const [isPlaying, setIsPlaying] = useState(false); const backgroundMode = useMemo(() => { if (typeof window === 'undefined') { return undefined; } return (window as CapacitorWindow).Capacitor?.Plugins?.BackgroundMode; }, []); useEffect(() => { const audio = audioRef.current; if (!audio) { return; } const handleEnded = () => { setIsPlaying(false); setStatus('Stream ended.'); }; const handleError = () => { setIsPlaying(false); setStatus('Stream error.'); }; audio.addEventListener('ended', handleEnded); audio.addEventListener('error', handleError); return () => { audio.removeEventListener('ended', handleEnded); audio.removeEventListener('error', handleError); }; }, []); useEffect(() => { if (!backgroundMode) { console.warn('BackgroundMode plugin is not available in this runtime.'); return; } const initializeBackgroundMode = async () => { try { const notificationPermission = await backgroundMode.checkNotificationsPermission(); if (notificationPermission.notifications !== 'granted') { await backgroundMode.requestNotificationsPermission(); } const microphonePermission = await backgroundMode.checkMicrophonePermission(); if (microphonePermission.microphone !== 'granted') { const requestedMicrophonePermission = await backgroundMode.requestMicrophonePermission(); if (requestedMicrophonePermission.microphone !== 'granted') { console.warn( 'BackgroundMode microphone permission denied; background playback may stop when screen is off.' ); } } const batteryOptimizations = await backgroundMode.checkBatteryOptimizations(); if (batteryOptimizations.enabled) { console.warn( 'Battery optimizations are enabled; requesting exemption for reliable background playback.' ); await backgroundMode.requestDisableBatteryOptimizations(); } await backgroundMode.disableWebViewOptimizations(); await backgroundMode.enable({ title: 'KRYZ-Go!', text: 'Background mode is active', channelName: 'kryz-go background', channelDescription: 'Keeps kryz-go running while the app is in background', resume: true, silent: false, disableWebViewOptimization: true, }); await backgroundMode.addListener('appInBackground', () => { console.log('kryz-go moved to background'); }); await backgroundMode.addListener('appInForeground', () => { console.log('kryz-go returned to foreground'); }); console.log('BackgroundMode plugin enabled.'); } catch (error) { console.error('Failed to initialize BackgroundMode plugin:', error); } }; void initializeBackgroundMode(); }, [backgroundMode]); const ensureBackgroundPlaybackPermissions = async () => { if (!backgroundMode) { return true; } const microphonePermission = await backgroundMode.checkMicrophonePermission(); if (microphonePermission.microphone === 'granted') { return true; } const requestedMicrophonePermission = await backgroundMode.requestMicrophonePermission(); if (requestedMicrophonePermission.microphone === 'granted') { return true; } setStatus('Enable microphone permission in Android settings for screen-off playback.'); console.warn('Background playback blocked: microphone permission not granted.'); return false; }; const startStream = async () => { const audio = audioRef.current; if (!audio) { return; } try { const hasBackgroundPermissions = await ensureBackgroundPlaybackPermissions(); if (!hasBackgroundPermissions) { return; } audio.src = STREAM_URL; await audio.play(); setIsPlaying(true); setStatus('Stream is playing.'); } catch (error) { setIsPlaying(false); setStatus('Unable to start stream.'); console.error('Failed to start stream:', error); } }; const stopStream = () => { const audio = audioRef.current; if (!audio) { return; } audio.pause(); audio.removeAttribute('src'); audio.load(); setIsPlaying(false); setStatus('Stream is stopped.'); }; return (

KRYZ Go!

Live Radio

{status}

); }