Added
-
π Customizable screensaver content (#47, #91, #61): The screensaver is no longer just a brightness dimmer. A new "Screensaver Style" selector in Settings β Display β Screensaver offers three modes: Dim Only (default, unchanged β just dims the brightness), Web Page (displays a read-only URL fullscreen β clock, dashboard, HTML; tap anywhere to wake), and Video / Image (plays a video/image playlist via the existing MediaPlayerComponent with loop, mute, fit modes). In URL and Video modes the current brightness is preserved (auto-brightness keeps working), so the screen stays visible and Android no longer turns it off β addressing wall-mounted panel use cases. Existing users are unaffected: defaults remain Dim Only. A warning is shown in settings if Screensaver Brightness is below 10% while URL/Video is selected.
-
π HTTP Basic Auth for WebView (#113): FreeKiosk now automatically responds to HTTP 401 authentication challenges using credentials configured in Settings β General β Website Authentication. Enter a username and password; the password is stored in the Android Keychain (never in plain text). Leave the username empty to disable. Implemented via the
basicAuthCredentialprop ofreact-native-webview, which hooks intoWebViewClient.onReceivedHttpAuthRequestnatively β no JavaScript injection required and no impact on sites that don't use Basic Auth. -
π Switch display mode via REST/MQTT (
setMode) (#76): A newsetModecommand allows switching between WebView mode and External App mode at runtime without restarting FreeKiosk. Send{"mode":"webview","url":"https://..."}to switch to WebView (theurlfield is optional), or{"mode":"external_app","package":"com.app.name"}to launch an app and activate the return overlay. Available via REST (POST /api/mode) and MQTT (set/mode). The transition is clean in both directions: switching to WebView stops the OverlayService and background monitor; switching to External App reads fresh overlay settings from storage to avoid stale closures, verifies the app is installed, starts the OverlayService, then launches the app. -
π― Motion detection sensitivity setting (#125): A new "Sensitivity" radio group (Low / Medium / High) is now visible in Settings β Display β Screensaver β Motion Detection when motion detection is enabled. Previously the sensitivity was hardcoded to Medium. Low sensitivity requires larger movements to trigger (15% pixel change threshold), Medium is the default (8%), and High reacts to subtle movements (4%). The selected value is persisted and applied immediately to the camera-based motion detector.
-
π FreeKiosk can be set as the default home launcher (#127):
MainActivitynow declares theHOME/DEFAULTintent categories alongsideLAUNCHER. This makes FreeKiosk appear in the Android "Choose home app" picker and allowsadb shell cmd package set-home-activity "com.freekiosk/.MainActivity"to succeed. Previously the command failed because no HOME intent-filter was declared on the main activity. This is optional β FreeKiosk only becomes the default launcher if the user (or ADB) explicitly selects it. -
π Keep Alive / Launch on Boot in Website mode (#37): Background apps (managed apps with "Keep Alive" or "Launch on Boot" enabled) can now be configured and monitored in Website (WebView) mode, not just App mode. A new "Background Apps" section appears in Settings β General when in Website mode. Use case: keep a music or audio receiver app (e.g. Spotify, Sendspin) alive in the background while displaying a web dashboard. The
BackgroundAppMonitorServicenow runs independently of the display mode and stops itself automatically if no keep-alive apps are configured. -
π£οΈ Web Speech API (speechSynthesis) polyfill (#NEW): Web apps running inside the WebView can now use the standard
window.speechSynthesis.speak()API for text-to-speech. Android WebView does not natively implement the Web Speech API (unlike Chrome), so FreeKiosk injects a transparent polyfill that bridgesspeechSynthesis.speak()βpostMessageβ native AndroidTextToSpeechengine. This means any web app that uses TTS (e.g. accessibility tools, notification readers, custom kiosk UIs) will work out of the box without code changes. Supportsspeak(),cancel(),getVoices(),onvoiceschanged,SpeechSynthesisUtterance(text, lang, rate, pitch, volume), and automatic language detection via the existing FreeKiosk TTS engine. The polyfill only activates when the nativespeechSynthesisis missing or non-functional (no voices). Also exposedHttpServerModule.speak()andHttpServerModule.stopSpeaking()as React Native bridge methods for direct native TTS access from JS. Requested by Carlos via email -
π¨οΈ Configurable print paper size (#NEW): A new "Default Paper Size" selector appears in Settings β General β Printing when printing is enabled. Available sizes: A4, A5, A3, Letter, Legal. The selected size is used as the default in the Android print dialog β the user can still override it manually if needed. Previously the print dialog always defaulted to A4 regardless of CSS
@pagerules. Reported by Paolo Leone via email
Fixed
-
β³ WebView stuck on infinite loading spinner for sites with redirect chains (#140): On sites that respond with HTTP redirects (e.g. a homepage redirecting to a login page),
onLoadStartfired once per redirect step and each call cleared and restarted the 10-second fallback timeout. If the site produced a rapid series of navigations (redirect chain, iframe loads, SPA internal routing), the countdown was continuously reset and never completed β leaving the loading overlay permanently on screen and hiding the actual page. Fixed by starting the fallback timer only once per loading session: if a timeout is already running, subsequentonLoadStartevents leave it untouched.onLoadEndandonLoadProgress === 1still cancel the timer normally when the page loads successfully. Reported by @SamuelSilvaG -
πΆ SSID showing "Wifi" instead of network name on Android 12+ devices (#80): On Android 12+ (API 31+),
WifiManager.connectionInfois deprecated and returns<unknown ssid>even when the location permission is granted and location services are enabled. Fixed by usingNetworkCapabilities.transportInfo(the recommended API since Android 10) to retrieve theWifiInfoobject on API 31+. Affects the REST API/api/info, MQTT status messages, and all system info endpoints. Reported by @hapishyguy -
π Lock Mode permanently disabled after using "Exit Kiosk Mode" (#124, #138): Calling "Exit Kiosk Mode" from the Advanced settings was writing
@kiosk_enabled=falseto AsyncStorage, which permanently disabled Lock Mode β on every subsequent launch, the kiosk started unlocked and users could escape via the home button. Root cause: thesetKioskEnabledInAsyncStorage(false)call was added as belt-and-suspenders for the watchdog fix (#96), but the watchdog is already stopped explicitly viastopService()before the activity finishes, making the write unnecessary. Fixed by removing the AsyncStorage write fromexitKioskMode()β@kiosk_enablednow staystrue, so kiosk mode re-engages on the next FK launch. The DE fast-boot flag is still cleared (soBootLockActivitydoes not hard-lock on next reboot), and the watchdog is still stopped explicitly (#96 fix unaffected). Reported by @mpreusse and @Mkdir1511 -
π£οΈ TTS silent for Chinese and other non-Latin languages even when language data is installed (#115): Root cause was that
TextToSpeech()was initialized without specifying an engine β Android may pick a default English-only engine rather than the engine the user configured in system settings (which has Chinese/Japanese/Korean support). Fixed by initializing TTS withSettings.Secure.getString("tts_default_engine")so FreeKiosk uses the same engine as the system TTS test page. Also improvedparseLocale()to useLocale.forLanguageTag()(proper BCP 47 parsing) instead of manual string splitting, fixing edge cases with tags likezh-CNorzh_CN. Also applied the same engine fix and added language auto-detection to MQTT'sspeakText()which previously spoke all text without ever callingsetLanguage(). Reported by @nowpast -
π Volume Button Alternative toggle not accessible in App mode (#110): The "Volume Button Alternative" toggle (Settings β Security β Return to Settings) was hidden when display mode was set to App. It is now visible in all modes. The toggle description in App mode now clarifies that the feature is active by default and may cause accidental PIN triggers during normal volume adjustment β users who do not need it in App mode can disable it here. Reported by @Mkdir1511
-
π Kiosk not locked during Android boot on low-end / slow devices (#98): After reboot, there was a window of 15β60 seconds where the device was unprotected while React Native loaded. This was partially fixed in v1.2.17 by
BootLockActivity(a pure-native activity that enters lock-task in under a second), but the fix was incomplete:ACTION_LOCKED_BOOT_COMPLETEDfires before Android decrypts CE (user-encrypted) storage, so the SQLite/AsyncStorage reads used to check kiosk settings all returned their safe default (false), preventingBootLockActivityfrom launching at all. Fixed by using DE (Device Encrypted)SharedPreferencesβ readable at any point during the boot process β to persist whether the fast-boot lock should activate. The DE flag is written: (a) wheneverstartLockTask/exitKioskModeis called from the app, and (b) at every normalACTION_BOOT_COMPLETEDso it stays in sync. AtACTION_LOCKED_BOOT_COMPLETED, only the DE flag is read (no SQLite). Additionally, ifBootLockActivitywas launched atLOCKED_BOOT_COMPLETEDbutMainActivity(which is not Direct Bootβaware) couldn't start yet, the activity now retries launchingMainActivityevery 5 seconds in its poll loop until CE storage becomes available. Reported by @rarcher -
π Launch on Boot causes infinite loop in External App mode (#37): Enabling "Launch on Boot" on a managed app (e.g. Velocity) while in External App mode caused the device to enter an unrecoverable loop β the screen cycled continuously between FreeKiosk and the external app until FreeKiosk was uninstalled. Three root causes fixed: (1)
launchBootApps()was called on everyloadSettings()invocation (including returns from Settings or PIN screen), not only on genuine app startup β abootAppsLaunchedRefguard now ensures it fires at most once per app session; (2)AppLauncherModule.launchBootApps()calledbringFreeKioskToFront()after launching the boot app, which triggeredMainActivity.onResume()fast-path whileloadSettingswas still running β this caused a second native relaunch of the external app and a double-start ofOverlayService, creating an unstable monitoring loop; (3)OverlayServiceis now started with the primary app's package beforelaunchBootApps()is called (single-app mode only), so kiosk protection is active from the moment the boot app appears in the foreground β no unprotected window. As a bonus,BootReceiver.launchMainActivityLegacy()no longer callsThread.sleep()on the main looper (replaced by a nestedpostDelayed), eliminating an ANR risk on Android 14+ devices. Reported by Tom Schiettecat (Hupac Intermodal) -
π PDF Viewer fails to load PDFs from CDN/WAF-protected servers (#BUG): PDFs hosted behind CDN/WAFs like Alibaba Cloud (e.g. byd.com) failed to load in the built-in PDF.js viewer with "Unable to load PDF" error. Three root causes fixed: (1) Missing Referer header β the native PDF proxy now injects
Referer: <pdf_url>when the WebView doesn't send one (which is always the case fromfile://origins), preventing WAF anti-hotlink blocks; (2) Lost session cookies βSet-Cookieheaders from proxied responses (e.g. Alibaba'sacw_tcWAF cookie) are now persisted back into Android'sCookieManagerso subsequent requests include them; (3) Range request failures β PDF.js was making multiple range requests that lost WAF session state between requests, now usesdisableRange: truefor a single full-download request for maximum compatibility. Also improved viewer error messages to include the truncated URL for easier debugging. Reported by Martin Lemke via email -
π PDF opened via popup (
window.open) blocked by URL filtering: When a website opened a PDF link viawindow.open()(popup), theonOpenWindowhandler checked URL filtering before checking for PDF interception β causing the PDF to be blocked in whitelist mode even with the PDF viewer enabled. PDF detection now runs first inonOpenWindow, consistent withonShouldStartLoadWithRequest. Reported by Martin Lemke via email -
π₯ Back button overlay disappears behind camera apps in fullscreen (#121): In External App / Multi-App mode, FreeKiosk's return overlay (back button and 5-tap exit zone) disappeared when camera apps (Google Camera, Open Camera) entered their fullscreen viewfinder mode. Root cause: camera apps use a hardware-accelerated
SurfaceViewwhose compositor layer can render aboveTYPE_APPLICATION_OVERLAYwindows after certain window state transitions. Fix:OverlayServicenow runs a periodic re-pin loop (every 3 s) while an external app is locked β it removes and immediately re-adds the overlay toWindowManager, placing it back at the top of the overlay stack. Also addedFLAG_HARDWARE_ACCELERATEDto overlay window params for correct compositing. Note: camera apps that callSurfaceView.setZOrderOnTop(true)permanently may still briefly occlude the overlay between re-pin cycles; the volume-button 5-tap exit (Settings β Security) remains available as an always-reachable alternative. Reported by @jmynes -
β¨οΈ Soft keyboard persists after screen sleep (#135): When a kiosk page had an input focused (e.g. Force Numeric mode on a login screen) and the device timed out, the soft keyboard remained visible on the next screen-on instead of being dismissed with the sleep event. Fixed by calling
InputMethodManager.hideSoftInputFromWindow()(+clearFocus()) inScreenStateReceiverwhenACTION_SCREEN_OFFis received. Reported by @asfreitas17 -
π± Display settings tab crashes on Android 8.x (StackOverflowError in Slider) (#86): Opening Settings β Display on Android 8.x (API 26β27) triggered an immediate
StackOverflowErrorand crashed the app. Root cause: on Android 8,AppCompatSeekBar.setMax()internally callssetProgressInternal()βrefreshProgress()βonProgressRefresh()βonProgressChanged()on the registered listener. The listener then calledseekbar.setProgress()to clamp the value, which re-triggeredonProgressChanged()recursively until the stack overflowed. An earlier fix attempt (v1.2.16-beta.1) added a JS-side local state wrapper inSettingsSliderto reduce parent re-renders during drag, but the crash happened at initialization time (when React Native appliesminimumValue/maximumValueprops via the native bridge) so the JS fix had no effect. Root fix: added amIsSettingProgressre-entrancy guard directly inReactSliderManager.onProgressChanged()via a patch-package patch on@react-native-community/slider@5.1.1β the guard returns immediately if re-entered, breaking the recursive loop. Reported by @gauthier-th