Classroom digital signage¶
- Author:
Zhenyu Yang <yangzhenyu@sust.edu.cn>
- Last updated:
Apr 2, 2026
Overview¶
The signage/ directory contains the Android classroom signage app used on
dedicated display devices. The current implementation is a native Kotlin kiosk
application. It runs in landscape mode, hides the system UI, scans a QR code to
bind itself to a classroom, and then pulls device-scoped signage data from the
Django backend.
Important
The current app is display-only.
It does not perform classroom check-in.
It does not call
face-service.It does not use the old anonymous
/signage/classrooms/{id}flow.
Repository layout¶
The signage app is a standalone Gradle project under signage/.
Key files in the current implementation:
signage/app/src/main/java/cn/edu/sust/myucspace/signage/MainActivity.ktfull-screen activity, QR scanning, device binding, and schedule displaysignage/app/src/main/java/cn/edu/sust/myucspace/signage/App.ktapplication entry that initializes device credential storagesignage/app/src/main/java/cn/edu/sust/myucspace/signage/storage/DeviceAuthStore.ktencrypted local storage for the API base URL and device tokensignage/app/src/main/java/cn/edu/sust/myucspace/signage/network/SignageApiService.ktRetrofit interface for signage device APIssignage/app/src/main/java/cn/edu/sust/myucspace/signage/repository/NetworkSignageRepository.ktdevice activation and signage data fetchingsignage/app/src/main/res/layout/activity_main.xmlmain signage screensignage/app/src/main/res/layout/dialog_camera.xmlQR binding dialog
Warning
Older documentation referenced an embedded Android WebView and
assets/index.html as the production runtime path. That is no longer the
active implementation.
The signage/web/ package may still exist in the repository as historical
code, but the current app does not render its UI through WebView.
How it works¶
Startup flow¶
On startup, MainActivity:
Enables immersive full-screen mode.
Initializes the signage UI and timeline components.
Checks whether a device token already exists in encrypted local storage.
If no token exists, opens the camera flow and waits for a setup QR code.
If a token exists, requests: -
GET /api/v1/signage/device-GET /api/v1/signage/device/scheduleIf the backend returns
401or403, clears the local device token and forces re-binding.
Device binding flow¶
The current signage app supports both activation-code binding and direct-token binding from a QR payload.
A management-side caller requests QR binding data from the backend.
The backend returns an activation-code payload containing
classroom_id,activation_codeandbase_url.The Android signage scans the QR payload.
The Android app loads or creates a local
device_codeand callsPOST /api/v1/signage/activatewhen the payload containsactivation_code.The app stores the device token in
EncryptedSharedPreferencesbacked by Android Keystore.
Supported QR payload formats in the current app:
JSON payload containing
activation_codeand optionalclassroom_idandbase_urlURL containing
activation_code/codeand explicitbase_urlraw activation code text
Backend API¶
The current signage device flow uses these endpoints:
POST /api/v1/signage/activatepublic endpoint for one-time activation code exchangeGET /api/v1/signage/deviceauthenticated device endpoint returning the current classroom signage configGET /api/v1/signage/device/schedule?start_date=YYYY-MM-DD&end_date=YYYY-MM-DDauthenticated device endpoint returning schedule items for the bound classroom
Management-side endpoints:
POST /api/v1/signage/classrooms/{id}/activation-codegenerate QR binding payloadPATCH /api/v1/signage/classrooms/{id}/configupdate signage configurationPOST /api/v1/signage/classrooms/{id}/revoke-tokenrevoke the current device tokenGET /api/v1/signage/classrooms/{id}admin-only signage device configuration lookupGET /api/v1/signage/classrooms/{id}/scheduleadmin-only classroom schedule lookup
Signage device data model¶
The device is represented by apps.signage.models.SignageDevice and is still
bound one-to-one with Classroom.
Important fields:
device_codedevice_labelsignage_titlesignage_subtitlesignage_configdevice_token_hashtoken_issued_atactivated_atlast_seen_atactivation_code_hashactivation_code_expires_at
Important
The backend stores only token hashes and activation-code hashes. The Android device stores the plaintext device token locally after activation.
Important behavior changes¶
The following changes are especially important when reading older docs or upgrading existing signage deployments:
The signage runtime is native Kotlin, not
WebView.The Android signage no longer uses a hard-coded classroom ID.
The Android signage no longer uses anonymous classroom-scoped signage APIs.
/api/v1/signage/classrooms/{id},/scheduleand/configare management endpoints and should not be called by signage devices directly.The signage app authenticates with a device token sent in the
Authorization: Bearer ...header.Management-side QR generation uses one-time activation codes.
Revoking a signage token immediately invalidates the bound device.
The current signage app does not perform sign-in, face recognition, or
face-serviceupload.
Building and running¶
Prerequisites¶
Android Studio, or Android SDK + command-line tools
A compatible JDK for the Android Gradle Plugin in use
This project uses a Gradle Version Catalog; see
signage/gradle/libs.versions.toml.
Build (Windows)¶
From the repo root:
cd signage
.\\gradlew.bat :app:assembleDebug
Build (Linux)¶
From the repo root:
cd signage
./gradlew :app:assembleDebug
Note
If you get a permission error, run chmod +x ./gradlew first.
The debug APK is typically generated under:
signage/app/build/outputs/apk/debug/
Install on a device¶
If adb is available:
adb install -r signage/app/build/outputs/apk/debug/app-debug.apk
Operational notes¶
BuildConfig.SIGNAGE_BASE_URLis the default API base URL used before the device is bound.The app may receive a different
base_urlfrom the scanned QR payload and persist it locally for subsequent requests.android.permission.CAMERAis required for initial device binding.android.permission.INTERNETis required for backend communication.android:allowBackupis disabled in the current manifest.android:usesCleartextTrafficis still enabled in the current manifest for local development convenience. Production deployments should use HTTPS and review whether cleartext traffic can be disabled.