Add Frontdesk kiosk APK build script and manufacturer flash guide.
Deploy Ladill Frontdesk / deploy (push) Successful in 59s

Ship OEM documentation for MDM, Elo, and Zebra lockdown plus a provisioning QR on kiosk device registration so factory tablets can pair on first boot.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-08 18:35:44 +00:00
co-authored by Cursor
parent e1dee12bc2
commit 60c2839553
5 changed files with 395 additions and 1 deletions
@@ -8,6 +8,7 @@ use App\Models\Branch;
use App\Models\Device;
use App\Models\ReceptionDesk;
use App\Services\Frontdesk\DeviceService;
use App\Services\Frontdesk\QrCodeService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
@@ -97,7 +98,7 @@ class DeviceController extends Controller
return redirect()->route('frontdesk.devices.index')->with('success', 'Device registered.');
}
public function edit(Request $request, Device $device): View
public function edit(Request $request, Device $device, QrCodeService $qrCodes): View
{
$this->authorizeAbility($request, 'devices.manage');
$this->authorizeOwner($request, $device);
@@ -107,6 +108,9 @@ class DeviceController extends Controller
'deviceTypes' => config('frontdesk.device_types'),
'branches' => $this->branches($request, $device->organization_id),
'desks' => $this->desks($request, $device->organization_id),
'kioskQrSvg' => $device->type === 'kiosk' && $device->device_token
? $qrCodes->kioskSvg($device->device_token, 220)
: null,
]);
}
+10
View File
@@ -33,6 +33,16 @@ class QrCodeService
return $this->renderSvg($this->employeeUrl($employee), $size);
}
public function kioskUrl(string $deviceToken): string
{
return route('frontdesk.kiosk.device', $deviceToken);
}
public function kioskSvg(string $deviceToken, int $size = 200): string
{
return $this->renderSvg($this->kioskUrl($deviceToken), $size);
}
protected function renderSvg(string $url, int $size = 200): string
{
$options = new QROptions([
+319
View File
@@ -0,0 +1,319 @@
# Ladill Frontdesk Kiosk — Manufacturer Flash Guide
This guide is for OEM partners flashing Ladill visitor-kiosk tablets before shipment. It covers APK installation, kiosk lockdown, and customer provisioning.
## Package summary
| Field | Value |
|-------|-------|
| App name | Ladill Frontdesk Kiosk |
| Package ID | `com.ladill.frontdesk.kiosk` |
| Min Android | 8.0 (API 26) |
| Target Android | 15 (API 35) |
| Default server | `https://frontdesk.ladill.com` |
| Factory admin PIN | `462834` (change after first provision) |
## Files you receive
| File | Purpose |
|------|---------|
| `ladill-frontdesk-kiosk-1.0.0.apk` | Signed release APK to flash on each device |
| This guide | Flash, lockdown, and QA checklist |
Forty8Tech builds the APK with:
```bash
./scripts/build-kiosk-apk.sh
```
Output path: `apps/android/dist/ladill-frontdesk-kiosk-<version>.apk`
The Android source tree lives under `apps/android/` locally and is not committed to git. Distribute the built APK to manufacturers directly (USB, SFTP, or MDM bundle).
---
## End-to-end flow
```mermaid
flowchart LR
A[Flash APK] --> B[Set device owner / MDM]
B --> C[Pin as HOME launcher]
C --> D[Customer registers kiosk in Frontdesk]
D --> E[Scan provisioning QR on tablet]
E --> F[Kiosk runs fullscreen]
```
1. **Factory:** Install APK, apply kiosk policy, set app as default HOME.
2. **Customer:** Registers a Visitor Kiosk under Ladill Frontdesk → Devices.
3. **Field / factory QA:** Scan the provisioning QR shown on the device edit screen.
4. **Runtime:** Tablet loads the web kiosk, sends heartbeats, and stays locked.
---
## 1. Generic pre-flash checklist
Before flashing a batch:
- [ ] Tablet is factory-reset (recommended for device-owner provisioning).
- [ ] Wi-Fi or Ethernet works; device can reach `https://frontdesk.ladill.com`.
- [ ] Date/time and timezone are correct (TLS and session cookies depend on this).
- [ ] Screen stays on when plugged in (disable aggressive battery saver).
- [ ] You have the signed APK for the target build version.
### Install APK (ADB)
```bash
adb install -r ladill-frontdesk-kiosk-1.0.0.apk
```
For system-privileged installs (some OEM images):
```bash
adb root
adb remount
adb push ladill-frontdesk-kiosk-1.0.0.apk /system/priv-app/LadillFrontdeskKiosk/LadillFrontdeskKiosk.apk
adb shell chmod 644 /system/priv-app/LadillFrontdeskKiosk/LadillFrontdeskKiosk.apk
adb reboot
```
---
## 2. Android Device Owner (generic MDM)
Device Owner mode is required for true kiosk lock (block Home, Recents, status bar). Use this for custom tablets or any MDM that supports Android Enterprise.
### 2.1 Provision device owner via MDM (recommended)
Device must have **no Google accounts** and ideally be fresh out of reset.
Use your MDMs **Android Enterprise device-owner** provisioning (QR, NFC, zero-touch, or StageNow). Assign `com.ladill.frontdesk.kiosk` as the **kiosk / lock-task** application.
The current APK uses immersive fullscreen, HOME launcher registration, and `startLockTask()` when the OS allows it. For strict device-owner policies (block Settings, prevent uninstall, silent install), provision through your EMM—do not rely on manual ADB unless your OEM documents a supported path.
> Need a build with a bundled `DeviceAdminReceiver` for `dpm set-device-owner`? Contact Forty8Tech for an OEM white-label package.
### 2.2 MDM policy (any EMM)
Configure these policies for `com.ladill.frontdesk.kiosk`:
| Policy | Setting |
|--------|---------|
| Kiosk / lock-task app | `com.ladill.frontdesk.kiosk` |
| Single app mode | Enabled |
| Default launcher | `com.ladill.frontdesk.kiosk` |
| Stay on launcher | Enabled |
| Status bar | Hidden or restricted |
| System navigation | Disabled |
| Auto-launch on boot | Enabled |
| Allowlist URLs | `*.ladill.com` |
| Camera | Allowed |
| Microphone | Allowed |
| Install unknown apps | Disabled |
| Factory reset | PIN-protected |
### 2.3 Set as default HOME launcher
After first launch:
1. Press Home (if visible during setup).
2. Choose **Ladill Frontdesk Kiosk**.
3. Select **Always**.
Or via ADB:
```bash
adb shell cmd package set-home-activity com.ladill.frontdesk.kiosk/.MainActivity
```
### 2.4 Boot behavior
The APK registers for `BOOT_COMPLETED` and starts automatically. Confirm:
```bash
adb shell am start -n com.ladill.frontdesk.kiosk/.MainActivity
adb reboot
# After boot, kiosk setup or web UI should appear without manual launch
```
---
## 3. Elo Touch Solutions
Typical on Elo I-Series and Open Frame Android panels with **EloView** or **Elo Access**.
### 3.1 EloView (cloud)
1. Add device to your EloView account.
2. Upload `ladill-frontdesk-kiosk-1.0.0.apk` as a managed app.
3. Create a **Kiosk** profile:
- **Primary app:** Ladill Frontdesk Kiosk (`com.ladill.frontdesk.kiosk`)
- **Mode:** Single app / lock task
- **Auto launch:** On boot
- **Navigation bar:** Hidden
- **Power button:** Map to sleep only (not exit kiosk)
4. Allow **Camera** and **Microphone** in the profile (visitor photo + QR checkout).
5. Push profile to device group before shipment.
### 3.2 Elo device settings (on-device)
1. Settings → **Elo Device Settings****Home App** → Ladill Frontdesk Kiosk.
2. Enable **Start on boot**.
3. Disable **Android Settings access** for field users.
4. Set **Display timeout** to maximum; keep **USB power** connected in lobby installs.
### 3.3 Elo QA
- [ ] App survives power cycle.
- [ ] No browser chrome visible.
- [ ] Camera preview works on check-in photo step.
- [ ] QR scanner works on checkout step.
---
## 4. Zebra Technologies
Typical on ET40, ET45, TC series, and custom Zebra Android builds with **StageNow** and **MX**.
### 4.1 StageNow barcode (recommended)
1. Open **StageNow** on a PC.
2. Create a **Setup** profile:
- Install APK from `file://` path or download URL (host APK on your CDN).
- Set **Default Launcher** to `com.ladill.frontdesk.kiosk`.
- Enable **Mx/kiosk mode** → single app = Ladill Frontdesk Kiosk.
3. Export profile as a barcode.
4. Scan on factory line during device setup.
### 4.2 MX KeyManager / AppMgr
| MX setting | Value |
|------------|-------|
| `AppMgr` install | `ladill-frontdesk-kiosk-1.0.0.apk` |
| `KioskMode` enabled | `true` |
| `KioskMode` package | `com.ladill.frontdesk.kiosk` |
| `UiMgr` hide status bar | `true` |
| `PowerMgr` stay awake on AC | `true` |
### 4.3 Zebra DataWedge (optional)
The kiosk uses the **web** QR scanner (`html5-qrcode`) for badge checkout. DataWedge is **not required**. If you remap a hardware scan trigger, ensure it does not steal focus from the WebView during checkout.
### 4.4 Zebra QA
- [ ] StageNow profile applies on clean device.
- [ ] Lock task survives suspend/resume.
- [ ] Wi-Fi roaming does not drop kiosk session.
---
## 5. Customer provisioning (after flash)
The tablet ships **unlinked** until the customer pairs it.
### 5.1 Customer steps (Ladill Frontdesk admin)
1. Sign in to [Frontdesk](https://frontdesk.ladill.com).
2. Go to **Devices → Add device**.
3. Type: **Visitor Kiosk**.
4. Save. Open the device → note the **Provision Ladill Kiosk app** QR code.
### 5.2 On-tablet steps
1. Power on tablet; Ladill Frontdesk Kiosk opens to **Provision kiosk**.
2. Tap **Show QR scanner** and scan the QR from Frontdesk.
- Or paste the device token / full kiosk URL manually.
3. Tap **Start kiosk**. The visitor check-in UI loads.
Provisioning URL format:
```
https://frontdesk.ladill.com/kiosk/d/{device_token}
```
Deep link (also supported):
```
ladillfrontdesk://kiosk/{device_token}
```
### 5.3 Verify online status
In Frontdesk → Devices, the kiosk should show **Online** within a few minutes (heartbeat every 5 minutes, plus activity on page load).
---
## 6. Admin access (factory and field service)
Hidden admin panel for re-provision, reload, and PIN change:
1. Tap the **top-left corner 7 times** within 2 seconds.
2. Enter admin PIN (factory default: `462834`).
3. Options: reload kiosk, re-provision, change PIN, exit admin.
**Change the factory PIN** before shipping to end customers, or document that the customer must change it during provisioning QA.
---
## 7. Security and signing
| Item | Guidance |
|------|----------|
| Factory keystore | Replace before mass production; do not ship factory passwords to end customers. |
| APK signature | All devices in a SKU should use the same release key for OTA updates. |
| Network | HTTPS only; app blocks mixed content. |
| Token storage | Device token is stored in EncryptedSharedPreferences on-device. |
| Token rotation | If customer regenerates token in Frontdesk, re-scan QR or re-provision. |
---
## 8. Factory QA checklist (per device)
| # | Test | Pass |
|---|------|------|
| 1 | APK installs without error | ☐ |
| 2 | App is default HOME launcher | ☐ |
| 3 | Boots directly into Ladill app after cold start | ☐ |
| 4 | Home / Recents blocked in kiosk mode | ☐ |
| 5 | Provisioning QR scan links device | ☐ |
| 6 | Visitor welcome screen loads | ☐ |
| 7 | Camera permission prompt accepted; photo step works | ☐ |
| 8 | Device shows Online in Frontdesk | ☐ |
| 9 | Admin PIN changed from factory default | ☐ |
| 10 | Device serial / IMEI recorded on build sheet | ☐ |
---
## 9. Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| “Device token required” / 404 | Invalid or revoked token | Re-scan QR from Frontdesk Devices |
| Stuck on setup screen | No network | Connect Wi-Fi; verify `frontdesk.ladill.com` reachable |
| Camera not working | Permission denied | Re-grant camera in admin → reload; check MDM camera policy |
| Device shows Offline | Network or heartbeat blocked | Allow `POST /api/devices/heartbeat`; disable battery optimization for app |
| User can exit to Settings | Lock task not active | Apply MDM kiosk policy; set device owner |
| White screen after provision | TLS or DNS issue | Check date/time; verify HTTPS certificate trust |
| Regenerated token | Old token invalid | Re-provision from admin panel or scan new QR |
### Useful ADB commands
```bash
# Logs
adb logcat -s LadillFrontdeskKiosk
# Clear app data (forces re-provision)
adb shell pm clear com.ladill.frontdesk.kiosk
# Check lock task state
adb shell dumpsys activity activities | grep mLockTaskMode
```
---
## 10. Contact
For white-label APK signing, device-admin receiver builds, or OEM-specific MX/Elo profiles:
**Forty8Tech / Ladill** — hardware@ladill.com
Include: tablet model, Android version, MDM platform, batch size, and target market.
@@ -41,6 +41,13 @@
<code class="mt-1 block break-all text-xs text-slate-700">{{ $device->device_token }}</code>
@if ($device->type === 'kiosk')
<a href="{{ route('frontdesk.kiosk.device', $device->device_token) }}" target="_blank" class="mt-2 inline-block text-sm text-indigo-700 hover:underline">Open kiosk URL</a>
@if (! empty($kioskQrSvg))
<div class="mt-4 rounded-xl border border-slate-200 bg-white p-4 text-center">
<p class="text-sm font-medium text-slate-900">Provision Ladill Kiosk app</p>
<p class="mt-1 text-xs text-slate-500">Scan with the tablet on first boot to link this device.</p>
<div class="mx-auto mt-3 inline-block">{!! $kioskQrSvg !!}</div>
</div>
@endif
@endif
</div>
@endif
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ANDROID_DIR="$ROOT/apps/android"
OUTPUT_DIR="$ANDROID_DIR/dist"
KEYSTORE_FILE="$ANDROID_DIR/.factory-release.keystore"
KEYSTORE_PROPS="$ANDROID_DIR/keystore.properties"
mkdir -p "$OUTPUT_DIR"
if [[ ! -f "$KEYSTORE_PROPS" ]]; then
if [[ ! -f "$KEYSTORE_FILE" ]]; then
echo "Generating factory release keystore at $KEYSTORE_FILE"
keytool -genkeypair \
-alias ladill-frontdesk-kiosk \
-keyalg RSA \
-keysize 4096 \
-validity 10000 \
-keystore "$KEYSTORE_FILE" \
-storepass "change-me-before-production" \
-keypass "change-me-before-production" \
-dname "CN=Ladill Frontdesk Kiosk, OU=Hardware, O=Forty8Tech, L=Accra, ST=Greater Accra, C=GH"
fi
cp "$ANDROID_DIR/keystore.properties.example" "$KEYSTORE_PROPS"
fi
if [[ -z "${ANDROID_HOME:-}" && -d "$HOME/Library/Android/sdk" ]]; then
export ANDROID_HOME="$HOME/Library/Android/sdk"
fi
if [[ -z "${JAVA_HOME:-}" ]]; then
if /usr/libexec/java_home -v 17 >/dev/null 2>&1; then
export JAVA_HOME="$(/usr/libexec/java_home -v 17)"
fi
fi
cd "$ANDROID_DIR"
./gradlew --no-daemon clean assembleRelease
APK_SRC="$ANDROID_DIR/app/build/outputs/apk/release/app-release.apk"
APK_DEST="$OUTPUT_DIR/ladill-frontdesk-kiosk-$(grep versionName app/build.gradle.kts | head -1 | sed -E 's/.*"([^"]+)".*/\1/').apk"
cp "$APK_SRC" "$APK_DEST"
echo ""
echo "Built release APK:"
echo " $APK_DEST"
echo ""
echo "Package: com.ladill.frontdesk.kiosk"
echo "Factory admin PIN: 462834 (change from kiosk admin after provisioning)"
echo ""
echo "See docs/KIOSK-MANUFACTURER-FLASH.md for full OEM flash instructions."