Releases: RushB-fr/freekiosk
FreeKiosk v1.2.18-beta.1 - Printing Support & Multi-Fix
Added
- 🖨️ Allow Printing toggle (#NEW): New "Allow Printing" setting (off by default) in General → Printing that enables
window.print()support in kiosk mode. When enabled: (1) thewindow.print()JavaScript call is intercepted and routed to Android's nativePrintManager, (2) print spooler packages (com.android.printspooler+ all installed print services like Samsung Print, HP Print, etc.) are dynamically discovered viaqueryIntentServicesand automatically whitelisted in Lock Task mode so the system print dialog can appear, (3) immersive mode is suspended while the print dialog is open and re-applied after it closes, (4)onResumelock task re-entry is deferred during printing to avoid killing the print UI, and (5)data:URLs are allowed in the WebView to support popup-based print flows (label printers, receipt generators). Supports WiFi, Bluetooth, USB printers and Save as PDF. Requested by @Poppy - ☀️ Auto-Brightness Offset (#92): New slider in Display → Auto-Brightness settings that lets you add a fixed percentage offset to the calculated auto-brightness value. For example, setting +10% means if the light sensor calculates 30% brightness, FreeKiosk will apply 40% instead. Useful when auto-brightness is consistently too dim for your environment but you still want it to adapt to ambient light. The offset is clamped at 100% maximum. Available in the settings UI (0–50% range with 5% steps) and via the REST API (
POST /api/autoBrightness/enablenow accepts an optionaloffsetparameter, 0–100). Requested by @Delivator
Fixed
- TTS silent for non-English text (#115):
/api/ttsonly spoke English and was completely silent for Chinese, Japanese, Korean and other non-Latin text becauseTextToSpeech.setLanguage()was never called — the engine defaulted to English. Added automatic language detection based on Unicode script analysis (CJK → Chinese, Hangul → Korean, Hiragana/Katakana → Japanese, Arabic, Thai, Hindi, Cyrillic, etc.). Also added an optionallanguageparameter (BCP 47 tag, e.g."zh-CN","ja","ko") for explicit control. The locale is now set before eachspeak()call. Requires the target language TTS voice data to be installed on the device. Reported by @nowpast - Some packages do not show up in app picker (#112): Packages without a launchable UI (services, VPN tools like gnirehtet, etc.) were excluded from the managed apps picker because
getInstalledApps()filtered ongetLaunchIntentForPackage() != null. Added a new native methodgetAllInstalledApps()that includes user-installed (non-system) packages even when they have no launcher activity. The managed apps picker now uses this method and offers a "Show all packages" toggle (off by default) to reveal background services/VPNs. Non-UI apps display a "service" badge for clarity. The single-app primary picker remains launcher-only since launching a non-UI package as the main app is not meaningful. Reported by @Royalflamejlh - ADB configuration doesn't support multi-app mode (#111): Added
external_app_modeandmanaged_appsADB intent extras to configure multi-app mode via ADB. You can now set--es external_app_mode "multi"and provide a JSON array of apps with--es managed_apps '[{"packageName":"com.app1"},{"packageName":"com.app2"}]'. Each app supportsshowOnHomeScreen,launchOnBoot,keepAlive, andallowAccessibilityflags. Both individual intent extras and full JSON config (--es config '{...}') are supported. Uninstalled packages are silently skipped. Display names are auto-resolved from the system if not provided. Reported by @Royalflamejlh - Volume buttons trigger PIN request when held (#110): Holding the volume button to adjust volume would trigger the PIN request because both
MainActivity.onKeyDownandVolumeChangeReceivercounted auto-repeat/rapid events as separate taps. Fixed by ignoringKeyEventwithrepeatCount > 0inMainActivity, and adding a minimum 250ms interval between counted volume changes inVolumeChangeReceiverto filter out the rapid events (~50-100ms) generated by holding the button. Deliberate separate presses (≥250ms apart) still work normally. Reported by @Mkdir1511 - Crash on boot with Lock Mode enabled (#109):
BootLockActivitycrashed immediately on boot with aNullPointerExceptioninhideSystemUI()becausewindow.insetsControllerwas called beforesetContentView(). On Android R+ (API 30+), this internally accesses theDecorViewwhich is only created bysetContentView()— so the DecorView wasnull. Fixed by reordering the calls sosetContentView()runs first, and added a try-catch safety net inhideSystemUI()for extra robustness on devices with unusual boot timing. Only affected v1.2.17-beta.1; v1.2.16 was unaffected because it didn't haveBootLockActivity. Reported by @sharkooon - Backup import from other devices / ADB push not working (#107): On Android 11+ (Scoped Storage), backup files pushed via
adb pushor copied from another device were invisible to the import list becauseRNFS.readDir()can only see files created by the app itself. Added a "Browse device for backup file..." button in the import modal that uses Android's Storage Access Framework (SAF) viaACTION_OPEN_DOCUMENTto open the native file picker — this bypasses Scoped Storage restrictions entirely. The JSON content is read directly throughContentResolver(no file copy needed). Also addedimportBackupFromContent()andparseBackupContent()toBackupServicefor content-based import/preview, and improved the empty-state message to guide users toward the browse button. Reported by @sharkooon - 📖 Device Owner setup incorrectly requires factory reset (#68): Updated all setup documentation (README, INSTALL.md, ADB_CONFIG.md, FAQ) to clarify that a factory reset is not required to activate Device Owner. Android's actual requirement is that no user accounts are active on the device — users can simply sign out of all accounts, run the
dpmcommand, and sign back in. Factory reset is now documented as a fallback only. Also added notes about SIM profiles/accounts that some devices retain. Reported by @realAllonZ, confirmed by @hapishyguy - WebView blocked by hosting providers (SiteGround, etc.): The hardcoded User-Agent (
Chrome/120.0.0.0onX11; Linux x86_64) was outdated and had a platform mismatch — hosting WAFs flagged it as a bot. Updated the default UA to a modern Chrome 131 on Android (Mozilla/5.0 (Linux; Android 13; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36). Also added a new Custom User Agent setting in Display settings, allowing users to override the UA string if specific sites require it - Screen Sleep Schedule not saving in App mode (#103): The Screen Sleep Scheduler settings (enabled, rules, wake-on-touch) were only persisted when in Website or Media Player mode. In External App mode, the save function forcibly reset
screenSchedulerEnabledtofalseand discarded all rules — even though the UI allowed configuring them in all modes. Moved scheduler save calls out of the mode-conditional block so they are now saved unconditionally, consistent with how they are loaded and executed. Reported by @hungrycactus - MQTT audio commands not working (#102):
audio_play,audio_beep, andaudio_stopMQTT commands were not functional while their REST API equivalents worked fine. The MQTT path forwarded audio commands to JS (ApiService.ts) which had no handler for them, whereas the REST API handled them natively in Kotlin (HttpServerModule). Added native audio handling (MediaPlayer,AudioTrack) directly inMqttModule.kt, matching the existing REST API implementation. Reported by @zeroping - MQTT still fails on older devices after #97 fix (#97): On older devices (Android 11 and below), R8 obfuscation of HiveMQ's internal classes (Dagger 2 IoC components, staged auth builder interfaces, lazy
InstanceHolderfactories) causedAbstractMethodErrororIncompatibleClassChangeErroron older ART runtimes when the auth code path was taken. Added comprehensive ProGuard keep rules for HiveMQ, Dagger, javax.inject, and RxJava. Hardened error handling withcatch (Throwable)(instead ofcatch (Exception)) to properly catchErrorsubclasses from Netty/Dagger static initialization and propagate them to the UI - Kiosk Watchdog not stopping on exit (#96): Fixed KioskWatchdogService continuing to run (and relaunching the app) after intentionally exiting kiosk mode. The watchdog now writes
@kiosk_enabled=falseto AsyncStorage and explicitly stops the service before closing the activity. Also clears the watchdog notification on exit. Reported by @krheinwald
FreeKiosk v1.2.17 - Boot Lock & Multi-App Mode
Added
-
🔒 Boot Lock Activity (#98): New lightweight native Android activity (
BootLockActivity) that enters lock-task mode immediately after boot — before React Native loads- Eliminates the window where users could interact with the OS freely on low-spec devices (e.g. Nokia C210) where RN can take 1-2 minutes to initialize
- Shows a minimal loading screen (app icon + spinner) and automatically hands off to MainActivity once React Native is ready
- Only activates for Device Owner installs with kiosk mode enabled; non-DO installs use the existing delayed-launch path
-
🛡️ Kiosk Watchdog Service (#96): New
KioskWatchdogServiceforeground service usingSTART_STICKYflag to survive OOM kills- On low-RAM devices (e.g. 2GB AndroidTV), automatically relaunches FreeKiosk within seconds if the kernel kills it due to memory pressure
- Includes relaunch cooldown (15s) to prevent relaunch storms
- Self-disables when kiosk mode is turned off; uses a silent minimal-priority notification
-
🎬 Media Player Mode: Brand-new display mode alongside Website and External App
- Play videos and images in full-screen kiosk mode with playlist support
- Local file support: Pick videos and images directly from the device via Android's native file picker; files are copied to app internal storage for reliable WebView playback; local files show a 📱 badge and filename in the playlist
- Native FilePickerModule: New Kotlin native module (
FilePickerModule.kt) usingACTION_OPEN_DOCUMENTintent; includesdeleteMediaFile,listMediaFiles,clearMediaFileshelpers - Remote URL support: Also accepts remote
http://andhttps://URLs for hosted media content - Playlist management: Add multiple media URLs or pick local files from General settings, with auto-detection of media type based on file extension or MIME type
- Video support: MP4, WebM, OGG formats with optional mute toggle
- Image support: PNG, JPG, GIF, SVG, WebP with configurable per-item or default display duration (seconds)
- Playback options: Auto-play, loop, shuffle, and optional on-screen controls (prev/play-pause/next with progress bar)
- Display options: Fit mode (contain/cover/fill), background color, crossfade transitions with configurable duration
- Full kiosk integration: Brightness control, screensaver, screen always on, status bar, lock mode, volume button return, and touch blocking all work identically to Website mode
- WebView-based rendering: Uses an embedded HTML5 player with
allowFileAccess,allowFileAccessFromFileURLs, andallowUniversalAccessFromFileURLsenabled for local file playback; dual-slot crossfade transitions - Error handling: Auto-skips unplayable items with retry; shows friendly empty state when no items configured
- Settings persistence: All media player settings saved to AsyncStorage, included in backup/restore, and properly reset
- Android 13+ permissions: Added
READ_MEDIA_VIDEOandREAD_MEDIA_IMAGESfor granular media access
-
📊 Dashboard Mode: New display mode that shows a configurable grid of URL tiles instead of a single WebView
- Users can create tiles with custom names and URLs, each automatically assigned a distinct color
- Tapping a tile opens its URL in the WebView with a navigation bar (back/forward/refresh/home)
- Configurable in Settings → Dashboard tab
-
📱 Multi-App Mode (#67): External App mode now supports managing multiple apps
- Add apps from the new "Managed Apps" section in General settings — each app appears on a home screen grid with icon circles
- All managed apps are automatically whitelisted in Lock Task Mode, so users can switch between them without escaping the kiosk
- The primary app (single package) still works exactly as before for backward compatibility
-
🚀 Launch App on Boot (#37): Managed apps with "Launch on Boot" enabled are automatically started in the background when the device boots, before FreeKiosk's own UI loads
- Combined with "Keep Alive", apps can be maintained as persistent background services
-
💓 Keep Alive Background Monitor (#37): New
BackgroundAppMonitorServiceforeground service checks every 30 seconds (viaUsageStatsManager) if managed apps with "Keep Alive" enabled are still running, and relaunches them if they've stopped or crashed- Starts automatically on boot when at least one keep-alive app is configured
-
♿ Accessibility Whitelist for Other Apps (#66): Device Owners can now allow other apps' accessibility services via a per-app "Allow Accessibility" toggle in Managed Apps settings
- Uses
DevicePolicyManager.setPermittedAccessibilityServices()to whitelist selected packages alongside FreeKiosk's own service - Applied at boot, on save, and when enabling via Device Owner
- Uses
-
⚙️ Android Settings Button (#89): New "Android System Settings" section in the Advanced tab
- Main button to open the native Android settings, plus quick-access shortcuts for WiFi, Sound, Display, Bluetooth, Date & Time, and Apps
- Fully compatible with Lock Task Mode: automatically pauses the lock, opens the settings, and re-engages kiosk mode when the user returns to FreeKiosk
- An info banner warns when kiosk mode is active
- Useful for devices with no physical navigation buttons where ADB commands are restricted by Admin mode
-
🔍 WebView Zoom Level (Display settings): New slider to control how web pages are rendered in WebView mode
- Range: 50%–200%, default 100% (matches Chrome's default rendering)
- Quick presets at 75%, 100%, 125%, 150%
- An info hint appears when zoom is not at default; only available in WebView mode
- Persisted to storage and included in backup/restore
Changed
- 🏪 Play Store Compliance: Conditional Self-Update (#playstore): In-app self-update via GitHub is now completely disabled when building for the Play Store
- A single Gradle flag (
-Pplaystore) controls everything at compile time — no separate codebase needed - When active:
REQUEST_INSTALL_PACKAGESpermission removed,UpdateInstallReceiverdisabled, entire "Updates" UI section hidden from Settings → Advanced, all native update methods become no-ops - R8 strips the dead update code from the final bytecode
- Normal sideload/F-Droid builds (
./gradlew assembleRelease) remain fully functional with self-update enabled - Play Store builds:
./gradlew bundleRelease -Pplaystore
- A single Gradle flag (
Fixed
-
🔌 MQTT Doesn't Connect When Password Is Set (#97): Fixed a crash in the password masking logic —
String.repeat(password.length - 6)produced a negative count for passwords shorter than 7 characters, throwingIllegalArgumentExceptionbefore the MQTT client was even built- This silently aborted
connect(), resulting in zero network traffic and an immediate return to "Disconnected" - Also fixed authentication being skipped entirely when a password was configured without a username
- This silently aborted
-
🔄 Inactivity Return Now Works in Dashboard Mode: Previously had no effect because the feature required a base URL (empty in dashboard mode)
- Now correctly returns to the dashboard grid after the configured timeout
-
🔄 URL Planner Return to Dashboard Grid: When a scheduled planner event ended while in Dashboard Mode, the app did not return to the dashboard grid due to a stale closure in the planner callback
- Fixed by using a ref to track the active event
-
♿ Accessibility Auto-Enable Fails with "Permission Denial: WRITE_SECURE_SETTINGS" (#99): The "Enable Automatically (Device Owner)" button crashed because
Settings.Secure.putString()requiresandroid.permission.WRITE_SECURE_SETTINGS, which is not automatically granted to Device Owners- Added the permission to the manifest, wrapped the secure settings write in a
SecurityExceptioncatch with a specificWRITE_SECURE_SETTINGS_REQUIREDerror code - Improved UI shows a clear dialog with the one-time ADB command:
adb shell pm grant com.freekiosk android.permission.WRITE_SECURE_SETTINGS BootReceiveralso handles the missing permission gracefully instead of crashing silently
- Added the permission to the manifest, wrapped the secure settings write in a
-
🔒 PIN Bypass via Back Gesture (#93): Fixed a security issue where swiping back (Android predictive back gesture) from the Settings or PIN screen could bypass PIN protection on Android 16+ devices (e.g. Lenovo Idea Tab Pro)
- Disabled swipe-back gestures on PIN and Settings screens
- Added
BackHandlerto block hardware/gesture back navigation - Replaced
navigation.navigatewithnavigation.resetto fully clear the navigation stack when returning to kiosk mode
-
📦 Self-Update Fails with "No Storage Permission" (#88): The APK download used
setDestinationInExternalPublicDir()which requiresWRITE_EXTERNAL_STORAGEruntime permission — never requested at runtime- Switched to
setDestinationInExternalFilesDir()(app-private directory), eliminating the storage permission requirement entirely - Old downloaded APKs are now cleaned up automatically before each new download
- Switched to
-
📦 Self-Update Fails to Install on Android 8+ (#88): APK install was blocked because "Install from unknown sources" was not enabled for the app
- Added a pre-download permission check (
canRequestPackageInstalls()) with a user-friendly dialog that opens the system settings page to grant the permission - On restricted devices (e.g. Amazon Echo Show / Fire OS) where the settings page doesn't exist, a clear fallback message guides the user to use
adb install -r <apk>instead
- Added a pre-download permission check (
-
🔧 Duplicate Import in HttpServerModule (#88): Removed duplicate
android.location.LocationManagerimport that caused Kotlin compilation failure -
🎮 Remote Control Now Works Natively Like a Physical Keyboard (#85): Remote key commands (
/api/remote/up,down,left,right,select,back,home,menu, `playpause...
FreeKiosk v1.2.16 - Screen Timeout & Hardware Sensors
Added
-
💤 "Keep Screen On" Toggle (#83): New option in Display settings to disable
FLAG_KEEP_SCREEN_ON- When turned off, the Android system manages screen timeout normally — the display turns off after the device's configured inactivity period, just like a regular device
- Default is ON (standard kiosk behavior — no change for existing users)
- Screensaver is automatically disabled and hidden when this option is off, since the system handles sleep
- Only available in WebView mode (External App mode already delegates screen management to the system)
- Included in backup/restore and reset
- REST API / MQTT
screensaverOncommand is ignored when keep-screen-on is disabled
-
📊 Device Hardware Info in MQTT & REST API (#80): Status now includes
manufacturer,model,androidVersion,apiLevel,processor,deviceName,product, anduptimefields- Home Assistant Discovery now registers 5 new sensors: Manufacturer, Model, Android Version, Processor, and Uptime
- Real device info in Home Assistant Discovery: Device block now shows actual manufacturer and model (e.g., "Samsung Galaxy Tab A") instead of hardcoded "FreeKiosk"
Fixed
-
♿ Accessibility Service Persistence (#80): Added
android:isAccessibilityTool="true"to prevent Android 12+ from auto-disabling the service after inactivity- Added automatic re-enablement on boot when app is Device Owner — the accessibility service is now programmatically re-enabled in
BootReceiverusing Device Owner privileges, surviving reboots without manual intervention
- Added automatic re-enablement on boot when app is Device Owner — the accessibility service is now programmatically re-enabled in
-
📡 MQTT Disconnects When App Goes to Background (#80): Added
AppStatelistener that detects when the app returns to foreground and automatically reconnects MQTT if the connection was lost during background/Doze mode- Devices now recover their MQTT connection seamlessly
-
🏷️ MQTT Device Name Not Updating (#80): Changing the Device Name in MQTT settings now prompts the user to reconnect, ensuring the new name takes effect in topics and Home Assistant discovery
- Previously the old name/ID persisted until a manual disconnect/connect cycle
-
🏷️ MQTT Device Name Pre-Filled with Device Model (#80): The Device Name field now auto-fills with the Android device model (e.g., "SM-T510", "Pixel 7") on first use, instead of generating a random hex ID
- Makes it easy to identify devices in a fleet
-
🧪 ExecuteJS Command Reliability (#80): Fixed
executeJs(via REST API and MQTT) silently failing when:- (a) the same JS code was sent twice in a row (React state didn't change)
- (b) the page was still loading
- Now appends a unique marker to force re-execution, and retries up to 5 seconds if the page is loading
-
📶 SSID Reporting Inconsistency (#80): Fixed
<unknown ssid>being passed through raw inSystemInfoModule(Status Bar)- Now consistently shows "WiFi" as fallback across all modules when location permission prevents SSID access
-
📷 Camera2 Fallback for Devices Where CameraX Fails Entirely (MediaTek LEGACY, front-only cameras): On some devices, CameraX's
CameraValidatorpermanently rejects the camera- Added a Camera2 API fallback: the settings screen now queries
Camera2directly via a newgetCamera2Devices()native method when CameraX returns nothing - The motion detector automatically switches to Camera2-based photo capture (
captureCamera2Photo()) when vision-camera has no device — enabling motion detection on hardware that CameraX cannot handle
- Added a Camera2 API fallback: the settings screen now queries
-
🐛 Fixed Crash (CalledFromWrongThreadException) When Entering Standby/Screensaver (#82): Native events (
onScheduledSleep,onScheduledWake,navigateToPin,onScreenStateChanged, API commands) were triggering React state updates synchronously on themqt_v_nativethread- Caused
react-native-screensto manipulate the Android view hierarchy from a non-UI thread - Wrapped all native event callbacks with
setTimeout(cb, 0)to defer state updates to the next event loop tick, ensuring React commits go through proper UI thread dispatch
- Caused
-
🐛 Fixed Invisible PIN Input on Dark Mode Devices (#81): The PIN
TextInputhad no explicitcolorset, so Android dark mode overrode the text/dot color to white — making input invisible against the white background- Added explicit
color: '#333333'andplaceholderTextColorto ensure dots and placeholder are always visible regardless of system theme
- Added explicit
FreeKiosk v1.2.15 - Brightness Control & Beta Channel
Added
-
💡 Allow System Brightness Management (#65): New toggle "App Brightness Control" in Display settings
- When disabled, FreeKiosk never touches screen brightness — system tools like Tasker, Android adaptive brightness, or other automation apps retain full control
- Applies to both WebView and External App modes
- All brightness-related UI (manual slider, auto-brightness, screensaver brightness) is hidden when disabled
- REST API brightness commands are also ignored when disabled
-
🧪 Beta Update Channel: Opt-in toggle to receive pre-release versions before stable releases
- New "🧪 Beta Updates" toggle in the Updates section (Settings → Advanced)
- When enabled, the in-app updater checks GitHub pre-releases (tagged
v1.2.15-beta.1, etc.) - When disabled (default), behavior is unchanged — only stable releases are shown
- Update alert shows a 🧪 badge and "(pre-release)" label for beta versions
- Semver-aware version comparison:
1.2.15-beta.1 < 1.2.15-beta.2 < 1.2.15(stable always wins) - No downgrade: switching beta OFF won't propose installing an older stable over a newer beta
Fixed
-
🔐 MQTT Password Field Adding Extra Characters: Removed custom bullet-masking logic in
SettingsInputand replaced with nativesecureTextEntry- Same fix as PinInput (v1.2.5)
- Custom masking reconstructed the real value from display text lengths, which broke with Samsung/Gboard predictive text, autocorrect, and paste, silently injecting extra characters
- Affects MQTT password, API key, and all other password fields using
SettingsInput
-
🔧 REST API Camera Photo Endpoint Returns "Invalid or Missing API Key" After Settings Change:
ApiSettingsSectionnow always restarts the HTTP server with the current stored settings when the REST API settings page is opened- Previously, if the server was started by
KioskScreenwith an API key that was later cleared in settings, the running server kept its stale config - Fixed a related race condition in the port/key/control change handlers where they checked a potentially-stale React
serverRunningstate instead of querying the native module
- Previously, if the server was started by
-
🔧 Motion Detection Shows "No Cameras Available" on Non-Standard SoCs (Rockchip, Amlogic, etc.): react-native-vision-camera's ProcessCameraProvider initializes asynchronously
- On slow hardware it resolves after the settings screen already read the empty camera list
- Fixed by subscribing to
CameraDevicesChangedso the UI updates as soon as cameras become available
FreeKiosk v1.2.14 - MQTT Fix
Added
-
🔌 MQTT Configuration via ADB Intents: Configure all MQTT settings headlessly for automated tablet provisioning
- 11 parameters supported:
mqtt_enabled,mqtt_broker_url,mqtt_port,mqtt_username,mqtt_password,mqtt_client_id,mqtt_base_topic,mqtt_discovery_prefix,mqtt_status_interval,mqtt_allow_control,mqtt_device_name - MQTT password stored securely in Android Keychain, same pattern as PIN
- Example usage:
adb shell am start -n com.freekiosk/.MainActivity \ --es mqtt_enabled "true" \ --es mqtt_broker_url "broker.local" \ --es mqtt_port "1883" \ --es mqtt_username "user" \ --es mqtt_password "pass"
- 11 parameters supported:
-
🔒 TLS/SSL MQTT Support: New
useTlsconfig option — auto-enabled when port is 8883 -
🔔 MQTT Connection Errors Surfaced to UI: Broker errors (e.g.
NOT_AUTHORIZED) now propagate from native Kotlin → JS → Settings UI- No more silent failures — connection issues are immediately visible in Settings
-
💾 Password Saved Hint: Shows "Password is saved" when a password is already configured
- Prevents accidental overwrites of existing credentials
Fixed
-
🔄 MQTT Reconnect Losing Credentials: HiveMQ's
automaticReconnect()was sendingnullusername/password on reconnection, causing the broker to reject withNOT_AUTHORIZED- Replaced with manual reconnect that always sends full credentials
- Exponential backoff: 1s → 30s between reconnection attempts
-
🏗️ Release Build Crash (R8/ProGuard): R8 obfuscation was renaming Netty/JCTools fields used via
Unsafe.objectFieldOffset()reflection, causingExceptionInInitializerErroron startup in release builds- Added official HiveMQ ProGuard rules (
-keepclassmembernames)
- Added official HiveMQ ProGuard rules (
-
📋 Password Paste Truncated to One Character: MQTT password field was incorrectly capturing only the last character on paste (
slice(-1)→slice(-charsAdded)) -
⌨️ Broker URL Keyboard Adding Spaces After Dots: Fixed by setting
keyboardType="url"on the broker URL input -
🔁 Connect Button ALREADY_RUNNING Error:
handleConnectnow stops the existing MQTT client before starting a new one -
🔄 External App Mode: Child Activities No Longer Killed by Auto-Relaunch (#69): Barcode scanners, file pickers, camera intents, and other child activities launched by the locked app are now properly detected and allowed
- Uses
ActivityManager.runningAppProcessesto check if the locked app's process is still alive (not crashed) — if alive and foreground is not a launcher, it's a child activity - Launchers (Home screen) are dynamically detected via
PackageManager.queryIntentActivities(ACTION_MAIN, CATEGORY_HOME)— works on all OEMs without hardcoding - Safe in Lock Task mode: user cannot open other apps, only the locked app can launch child activities
- Logic: launcher detected → relaunch FreeKiosk; locked app process alive → allow child activity; process dead → relaunch FreeKiosk
- Fixes use cases: MLKit barcode scanner, camera intents, file pickers, permission dialogs, any native modal launched by the locked app
- Uses
-
🚀 External App Mode Boot: REST API Now Starts Automatically: When FreeKiosk is set as default launcher in External App Mode,
HomeActivitynow also startsMainActivityin background- Ensures REST API server, MQTT, and other services are running even when an external app is in foreground
MainActivityautomatically moves to background (moveTaskToBack) when started fromHomeActivity- Fixes issue where the external app would start but FreeKiosk's API server wouldn't be accessible
FreeKiosk v1.2.13 - MQTT & Home Assistant Integration
Added
- 📡 MQTT + Home Assistant Auto-Discovery: Native MQTT client with full HA integration
- 27 auto-discovered entities in Home Assistant via MQTT Discovery protocol
- 11 sensors: Battery level, brightness, WiFi SSID, WiFi signal, light sensor, IP address, app version, memory used, storage free, current URL, volume
- 6 binary sensors: Screen on/off, screensaver active, battery charging, kiosk mode, device owner, motion detected
- 2 number controls: Brightness (0-100%), volume (0-100%) — adjustable sliders in HA
- 2 switches: Screen power (ON/OFF), screensaver (ON/OFF)
- 5 buttons: Reload, wake, reboot, clear cache, lock
- 1 text entity: Navigate URL — send a URL to load in the WebView
- 20 additional commands via MQTT: TTS, toast, audio play/stop/beep, launch app, execute JS, URL rotation start/stop, restart UI
- Push-based status: Periodic state publishing (configurable 5-3600 seconds, default 30s)
- LWT (Last Will & Testament): Automatic availability tracking — HA shows device as unavailable on disconnect
- Auto-reconnect: Handles WiFi drops and broker restarts with automatic re-publishing of all discovery configs
- Always-on Motion Detection: Configurable option to run camera-based motion detection continuously (not just during screensaver)
- Full command parity with REST API — both interfaces dispatch through the same command handler
- Concurrent operation: MQTT and REST API can run simultaneously
- Eclipse Paho MQTT v3.1.1 with secure password storage (Android Keychain)
- Settings: Broker URL, port, username, password, client ID, base topic, discovery prefix, status interval, allow control
- Connection status indicator in Settings UI
- MQTT settings included in backup/restore
- Full MQTT Documentation
Fixed
-
🔊 No Audio in Lock Mode on Samsung/OneUI Devices: Audio streams were muted by Samsung when
LOCK_TASK_FEATURE_NONEwas setLOCK_TASK_FEATURE_GLOBAL_ACTIONSis now included by default (matches Android's own default whensetLockTaskFeatures()is never called), preventing Samsung/OneUI from muting audio inLOCK_TASK_MODE_LOCKED- Added
AudioManagersafety net: after entering lock task mode,setMasterVolumeMuted(false)is called followed byADJUST_UNMUTEon all audio streams (MUSIC, NOTIFICATION, ALARM, RING) - Settings UI change: "Allow Power Menu" toggle renamed to "🔌 Block Power Menu" with inverted logic — power menu is now allowed by default, admin can explicitly block it if needed
- No migration required: same storage key
@kiosk_allow_power_button— existing user settings preserved; only new installs benefit from the new default - Applied consistently across
KioskModule.kt,MainActivity.kt, andAppLauncherModule.kt
-
🔧 Camera/Microphone Not Working in WebView on Fire OS (Echo Show, Fire tablets) (#63): Auto-grant WebView media/geolocation permissions in kiosk mode
- OS-level permission via
pm grantstill required - WebView now receives runtime permissions automatically when kiosk mode is active
- OS-level permission via
FreeKiosk v1.2.12 - Screen Lock & Accessibility Enhancements
Added
-
🔒 Screen Lock Without Device Owner:
screen/offandlocknow work with Device Admin or AccessibilityService- 4-tier fallback: Device Owner
lockNow()→ Device AdminlockNow()→ AccessibilityServiceGLOBAL_ACTION_LOCK_SCREEN(API 28+) → dim brightness to 0 dpm.lockNow()is available to Device Admin apps (API 8+), not just Device Owner — this was an oversight now corrected- Enables full FreeKiosk screen control when another MDM already holds Device Owner
- Truly turns off the screen (hardware off) with any of the first 3 tiers
- Wake-up cycle (
screen/on, AlarmManager, WakeLock) unchanged and fully compatible /api/lockandscreen/offresponse now includes"method"field ("DeviceOwner","DeviceAdmin", or"AccessibilityService")
- 4-tier fallback: Device Owner
-
📄 Inline PDF Viewer: PDFs now open directly in-app via a bundled PDF.js viewer instead of being downloaded
- Enabled via a toggle in Settings → General → PDF Viewer
- Uses PDF.js v3.11.174 bundled locally in Android assets — no Google Docs, no external service
- Full viewer UI: page navigation (◀/▶), zoom (−/⊡/+), close (✕), and download (⬇) buttons
- Download button triggers the native Android DownloadManager (notification + Downloads folder)
- Intercepts PDF links at 3 levels:
- JS injection: strips
<a download>attributes so Android's DownloadListener doesn't fire early onShouldStartLoadWithRequest: redirects.pdfURLs and Google redirect URLs (google.com/url?url=...) to the viewer- Native
DownloadListenerpatch: intercepts PDFs detected byContent-Type: application/pdforContent-Disposition: attachmentand loads the viewer instead of downloading
- JS injection: strips
- Native HTTP proxy (
shouldInterceptRequest): when the viewer is active, proxies all remote PDF XHR requests viaHttpURLConnectionto bypass CORS restrictions, forwarding cookies andRangeheaders - Security:
allowFileAccess/allowUniversalAccessFromFileURLsonly enabled when PDF viewer is on - All patches saved in
patches/react-native-webview+13.16.0.patchviapatch-package
-
♿ AccessibilityService for Cross-App Key Injection: New
FreeKioskAccessibilityServiceenables keyboard emulation in External App mode- Uses
performGlobalAction()for Back/Home/Recents navigation (all Android versions) - Uses
InputMethod.sendKeyEvent()/commitText()for keys and text on Android 13+ (API 33+) - Fallback for Android 5–12:
ACTION_SET_TEXTinjects printable characters, text, Backspace, and Shift+letter KeyCharacterMapconverts keyCodes to printable characters for the ACTION_SET_TEXT fallback- Automatic fallback chain: AccessibilityService → Activity
dispatchKeyEvent()→input keyevent(last resort) - Settings UI: New "Accessibility Service" section in Advanced Settings with:
- Status indicator (Active / Enabled / Disabled)
- "Open Accessibility Settings" button to launch Android's settings
- "Enable Automatically" button for Device Owner mode (no user interaction needed)
- Info box explaining why the service is needed
- Compatible with privacy ROMs (e/OS, LineageOS, CalyxOS, GrapheneOS) where Instrumentation is blocked
- Uses
Fixed
- 🔑 Key Injection Compatibility Fix: Replaced
Instrumentation.sendKeyDownUpSync()withactivity.dispatchKeyEvent()across all remote/keyboard endpointsInstrumentationrequiresINJECT_EVENTS(signature-level permission) which privacy-focused ROMs (e/OS, LineageOS, CalyxOS, GrapheneOS) blockdispatchKeyEvent()dispatches directly into the Activity's View hierarchy — no special permission needed- Affects:
/api/remote/*(all 9 keys),/api/remote/keyboard/{key},/api/remote/keyboard?map=...,/api/remote/text - Also fixed in
KioskModule.sendRemoteKey()(used by JS-side remote control) - No regression on standard ROMs (Samsung, Pixel, AOSP)
FreeKiosk v1.2.11 - Remote Control & Scheduler Fixes
Added
-
⌨️ Keyboard Emulation API: Full keyboard input simulation via REST API
- Single key press (
GET|POST /api/remote/keyboard/{key}): Send any keyboard key- Supports: a-z, 0-9, F1-F12, space, tab, enter, escape, backspace, delete, arrows, symbols, media keys
- Over 80 named keys + single character support
- Keyboard shortcuts (
GET|POST /api/remote/keyboard?map=ctrl+c): Send key combinations with modifiers- Supports: ctrl, alt, shift, meta (Windows/Cmd key)
- Examples:
ctrl+c,ctrl+v,alt+f4,ctrl+shift+a
- Text input (
POST /api/remote/text): Type full text strings into focused input fields- Body:
{"text": "Hello World!"} - Uses
Instrumentation.sendStringSync()for natural text input
- Body:
- All keyboard operations handled natively (no JS bridge — fast and reliable)
- Single key press (
-
📍 GPS Location API (
GET /api/location): New endpoint for device GPS coordinates- Returns: latitude, longitude, accuracy, altitude, speed, bearing, provider, timestamp
- Uses GPS, Network, and Passive location providers (best accuracy wins)
- Permissions already declared in manifest
-
🔋 Enriched Battery API:
GET /api/batterynow returns additional data- New fields:
temperature(°C),voltage(V),health(good/overheat/dead/etc.),technology(Li-ion/etc.) - Backward compatible: existing
level,charging,pluggedfields unchanged
- New fields:
-
🔒 Lock Device API (
GET|POST /api/lock): New endpoint to lock the device screen- Uses
DevicePolicyManager.lockNow()for a true screen lock (Device Owner required) - Returns clear error message if Device Owner mode is not active
- Uses
-
🔄 Restart UI API (
GET|POST /api/restart-ui): New endpoint to restart the app UI- Calls
activity.recreate()to fully restart the React Native activity - Useful for remote troubleshooting without rebooting the device
- Calls
-
🗣️ Text-to-Speech (TTS): Fully implemented native TTS via Android
TextToSpeechengine- TTS engine is initialized when the HTTP server starts
- Handled natively (no JS bridge dependency — works even if React Native is unresponsive)
- Auto-retries if TTS engine is not ready on first call
-
📊 Volume Read API (
GET /api/volume): New endpoint to read current volume level- Returns
{ level: 0-100, maxLevel: 100 }for easy integration with Home Assistant sensors
- Returns
Fixed
-
🐛 Screen Sleep Scheduler - Black Screen & Navigation Lockout: Fixed 4 critical bugs causing scheduler to malfunction
- Feedback loop: Scheduler re-entered sleep immediately after wake due to
isScheduledSleepin useEffect dependency array - Navigation lockout: Scheduler interval kept running while on PIN/Settings screen, calling
lockNow()and locking user out - Wake-on-touch broken: Touch events during sleep did nothing — never restored brightness or called
exitScheduledSleep() - Stale closure:
checkScreenSchedule()used outdated state variable instead of ref - N-tap during sleep: 5-tap for settings now properly exits scheduled sleep before navigating to PIN
- Activity null after lockNow():
turnScreenOn()now acquires WakeLock before checking for activity availability - Fixes black screen issue on Android 8.1+ and impossible settings access during sleep windows
- Feedback loop: Scheduler re-entered sleep immediately after wake due to
-
🐛 Power Menu Dismissed Immediately on Some Devices (TECNO/HiOS): Fixed GlobalActions (power menu) being closed ~900ms after appearing when "Allow Power Button" is enabled in Lock Mode
- Root cause:
onWindowFocusChangedaggressively re-applied immersive mode, stealing focus back from the system power menu window - Additionally,
onResumewould re-triggerstartLockTask()during the brief focus transition, compounding the issue - Fix: debounced
hideSystemUI()by 600ms on focus regain, and deferredstartLockTask()re-lock when power button is allowed and focus was recently lost - No security impact: Lock Task Mode remains fully active throughout — only the cosmetic immersive mode re-application is delayed
- Affects TECNO, Infinix, itel (HiOS) and potentially other OEMs with aggressive WindowManager behavior on Android 14+
- Root cause:
-
🐛 Device Owner Status Hardcoded
falsein API: Fixed/api/infoand/api/statusalways reportingisDeviceOwner: false- Was hardcoded to
falseinHttpServerModule.getDeviceStatus() - Now performs a real
DevicePolicyManager.isDeviceOwnerApp()check - This caused external dashboards to incorrectly show Device Owner as inactive
- Was hardcoded to
-
📺 Screen On Not Working After lockNow(): Fixed
GET /api/screen/onfailing when screen was offreactContext.currentActivitywasnullafterlockNow()and the code silently did nothing- WakeLock is now acquired before checking for activity (WakeLock works without activity)
- Added keyguard dismissal to properly wake from locked state
- Screen now reliably turns on whether activity is available or not
-
🧹 Clear Cache Now Actually Clears: Fixed
/api/clearCachewhich only reloaded the WebView- Now performs a full native cache clear: WebView HTTP cache, cookies, Web Storage (localStorage/sessionStorage), form data
- Then forces a WebView remount on the JS side for a complete fresh start
-
🔄 In-App Update 404 Error: Fixed update download failing with 404 error
- Now retrieves actual APK download URL from GitHub release assets instead of constructing it
- Eliminates filename case sensitivity issues (FreeKiosk vs freeKiosk)
- More robust: works regardless of APK naming convention changes
- Fallback to constructed URL if asset parsing fails
-
📸 Screenshot Race Condition: Fixed
/api/screenshotreturning 503 intermittently- Replaced
Thread.sleep(100)with a properCountDownLatchto wait for the UI thread - Screenshot capture now waits up to 5 seconds for the UI thread to complete
- Replaced
FreeKiosk v1.2.10 - URL Filtering & Backup Improvements
Added
-
⏱️ Inactivity Return - Scroll to Top Toggle: New optional behavior for when already on start page
- Added "Scroll to Top on Start Page" toggle (enabled by default)
- When enabled and already on start page, smoothly scrolls to top instead of doing nothing
- Improves user experience for single-page applications
-
🔗 URL Filtering (Blacklist / Whitelist): Control which URLs users can navigate to within the kiosk WebView
- Choose between Blacklist mode (block specific URLs) or Whitelist mode (allow only specific URLs)
- Wildcard pattern support (e.g.,
*.example.com/*,freekiosk.app/download) - Patterns without protocol are automatically matched with
http://andhttps:// - Main kiosk URL is always protected and cannot be blocked
- Empty whitelist = strictest mode (only main URL allowed)
- Works with both traditional navigation and SPA/client-side routing (pushState)
- Optional visual feedback toast when a URL is blocked
- Popup/new window URLs are also filtered
- New settings section in WebView tab: "URL Filtering"
Fixed
-
🔗 URL Filtering - Form Submits and JS Buttons: Fixed form submissions and JavaScript buttons being blocked in whitelist mode
- Filter now compares origin + pathname instead of just origin
- Same-page navigations (query params, hash changes, form submits) are always allowed
- Trailing slashes are normalized (e.g.,
https://example.comandhttps://example.com/are treated as identical) - Only navigation to different pages on the same domain requires whitelist match
-
📡 NFC Monitoring Fix: Fixed "flicking back to blue screen" when NFC is enabled in kiosk mode
- Foreground monitoring detected transient
com.android.nfcpackage as a wrong app and triggered a relaunch loop - NFC system package is now filtered from monitoring checks only when NFC mode is active
- No impact on monitoring behavior when NFC is disabled
- Foreground monitoring detected transient
-
💾 Backup/Restore Missing Settings: Fixed 20 settings keys not being included in export/import backups
- Added missing URL filtering settings (blacklist/whitelist lists and configuration)
- Added missing screen scheduler, inactivity return, blocking overlays settings
- Added missing WebView back button, camera position, return-to-settings preferences
- PIN mode setting now properly backed up and restored
- Ensures complete configuration portability across devices
FreeKiosk v1.2.9 - Rotation & Loading Fixes
Fixed
-
📱 Status Bar Rotation Fix: Fixed custom status bar disappearing after screen rotation in external app mode
- OverlayService now recreates the status bar overlay after configuration changes
- MainActivity re-hides Android system bars on rotation to prevent them from reappearing
- Ensures consistent UI across orientation changes
-
🔧 Lock Mode "Device Owner not configured" False Warning: Fixed JS bundle out of sync with native Kotlin module
startLockTaskcall in bundled JS had 2 parameters instead of 3 (missingallowNotifications)- React Native bridge could not match the method signature, causing a silent exception
- Resulted in false "Device Owner not configured" warning even when Device Owner was properly set
- Lock mode now activates correctly without false error messages
-
🖱️ 5-Tap During Page Load: Fixed 5-tap not working while WebView is loading or when page fails to load
- Invisible touch zone in bottom-right corner during loading and error states
- Tapping it counts as a 5-tap interaction, allowing access to settings even without network
- Touch zone disappears automatically once the page loads successfully (JS-based detection takes over)
- Ensures settings remain accessible in all WebView states