.. SPDX-License-Identifier: GPL-3.0-or-later ========================= Classroom digital signage ========================= :Author: Zhenyu Yang :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.kt`` full-screen activity, QR scanning, device binding, and schedule display - ``signage/app/src/main/java/cn/edu/sust/myucspace/signage/App.kt`` application entry that initializes device credential storage - ``signage/app/src/main/java/cn/edu/sust/myucspace/signage/storage/DeviceAuthStore.kt`` encrypted local storage for the API base URL and device token - ``signage/app/src/main/java/cn/edu/sust/myucspace/signage/network/SignageApiService.kt`` Retrofit interface for signage device APIs - ``signage/app/src/main/java/cn/edu/sust/myucspace/signage/repository/NetworkSignageRepository.kt`` device activation and signage data fetching - ``signage/app/src/main/res/layout/activity_main.xml`` main signage screen - ``signage/app/src/main/res/layout/dialog_camera.xml`` QR 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``: 1. Enables immersive full-screen mode. 2. Initializes the signage UI and timeline components. 3. Checks whether a device token already exists in encrypted local storage. 4. If no token exists, opens the camera flow and waits for a setup QR code. 5. If a token exists, requests: - ``GET /api/v1/signage/device`` - ``GET /api/v1/signage/device/schedule`` 6. If the backend returns ``401`` or ``403``, 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. 1. A management-side caller requests QR binding data from the backend. 2. The backend returns an activation-code payload containing ``classroom_id``, ``activation_code`` and ``base_url``. 3. The Android signage scans the QR payload. 4. The Android app loads or creates a local ``device_code`` and calls ``POST /api/v1/signage/activate`` when the payload contains ``activation_code``. 5. The app stores the device token in ``EncryptedSharedPreferences`` backed by Android Keystore. Supported QR payload formats in the current app: - JSON payload containing ``activation_code`` and optional ``classroom_id`` and ``base_url`` - URL containing ``activation_code`` / ``code`` and explicit ``base_url`` - raw activation code text Backend API =========== The current signage device flow uses these endpoints: - ``POST /api/v1/signage/activate`` public endpoint for one-time activation code exchange - ``GET /api/v1/signage/device`` authenticated device endpoint returning the current classroom signage config - ``GET /api/v1/signage/device/schedule?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD`` authenticated device endpoint returning schedule items for the bound classroom Management-side endpoints: - ``POST /api/v1/signage/classrooms/{id}/activation-code`` generate QR binding payload - ``PATCH /api/v1/signage/classrooms/{id}/config`` update signage configuration - ``POST /api/v1/signage/classrooms/{id}/revoke-token`` revoke the current device token - ``GET /api/v1/signage/classrooms/{id}`` admin-only signage device configuration lookup - ``GET /api/v1/signage/classrooms/{id}/schedule`` admin-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_code`` - ``device_label`` - ``signage_title`` - ``signage_subtitle`` - ``signage_config`` - ``device_token_hash`` - ``token_issued_at`` - ``activated_at`` - ``last_seen_at`` - ``activation_code_hash`` - ``activation_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}``, ``/schedule`` and ``/config`` are 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-service`` upload. 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: .. code-block:: powershell cd signage .\\gradlew.bat :app:assembleDebug Build (Linux) ------------- From the repo root: .. code-block:: bash 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: .. code-block:: bash adb install -r signage/app/build/outputs/apk/debug/app-debug.apk Operational notes ================= - ``BuildConfig.SIGNAGE_BASE_URL`` is the default API base URL used before the device is bound. - The app may receive a different ``base_url`` from the scanned QR payload and persist it locally for subsequent requests. - ``android.permission.CAMERA`` is required for initial device binding. - ``android.permission.INTERNET`` is required for backend communication. - ``android:allowBackup`` is disabled in the current manifest. - ``android:usesCleartextTraffic`` is still enabled in the current manifest for local development convenience. Production deployments should use HTTPS and review whether cleartext traffic can be disabled.