Skip to content

教室数字信息屏

作者: Zhenyu Yang yangzhenyu@sust.edu.cn | 最后更新: 2026年4月2日

概述

signage/ 目录包含用于专用显示设备的 Android 教室信息屏应用。当前实现是一个原生 Kotlin Kiosk 应用。它以横屏模式运行,隐藏系统 UI,通过扫描二维码绑定到教室,然后从 Django 后端拉取设备维度的信息屏数据。

重要说明

当前应用仅用于展示

  • 执行课堂签到。
  • 调用 face-service
  • 使用旧的匿名 /signage/classrooms/{id} 流程。

仓库目录结构

信息屏应用是 signage/ 下的一个独立 Gradle 项目。

当前实现中的关键文件:

  • signage/app/src/main/java/cn/edu/sust/myucspace/signage/MainActivity.kt — 全屏 Activity,二维码扫描、设备绑定和课程表展示
  • signage/app/src/main/java/cn/edu/sust/myucspace/signage/App.kt — 应用入口,初始化设备凭证存储
  • signage/app/src/main/java/cn/edu/sust/myucspace/signage/storage/DeviceAuthStore.kt — 用于存储 API 基础 URL 和设备令牌的加密本地存储
  • signage/app/src/main/java/cn/edu/sust/myucspace/signage/network/SignageApiService.kt — 信息屏设备 API 的 Retrofit 接口
  • signage/app/src/main/java/cn/edu/sust/myucspace/signage/repository/NetworkSignageRepository.kt — 设备激活和信息屏数据获取
  • signage/app/src/main/res/layout/activity_main.xml — 信息屏主界面
  • signage/app/src/main/res/layout/dialog_camera.xml — 二维码绑定对话框

WARNING

较早的文档曾引用嵌入式 Android WebViewassets/index.html 作为生产运行时路径。这已不再是当前的实现方式。

signage/web/ 包可能仍作为历史代码存在于仓库中,但当前应用不通过 WebView 渲染 UI。

工作原理

启动流程

启动时,MainActivity 依次执行:

  1. 启用沉浸式全屏模式。
  2. 初始化信息屏 UI 和时间轴组件。
  3. 检查加密本地存储中是否已存在设备令牌。
  4. 若不存在令牌,打开相机流程等待扫描设置二维码。
  5. 若存在令牌,发起请求:
    • GET /api/v1/signage/device
    • GET /api/v1/signage/device/schedule
  6. 若后端返回 401403,清除本地设备令牌并强制重新绑定。

设备绑定流程

当前信息屏应用支持激活码绑定和二维码载荷中的直接令牌绑定两种方式。

  1. 管理端调用方向后端请求二维码绑定数据。
  2. 后端返回包含 classroom_idactivation_codebase_url 的激活码载荷。
  3. Android 信息屏扫描二维码载荷。
  4. Android 应用加载或创建本地 device_code,当载荷包含 activation_code 时调用 POST /api/v1/signage/activate
  5. 应用将设备令牌存储在由 Android Keystore 支持的 EncryptedSharedPreferences 中。

当前应用支持的二维码载荷格式:

  • 包含 activation_code 以及可选的 classroom_idbase_url 的 JSON 载荷
  • 包含 activation_code / code 和显式 base_url 的 URL
  • 原始激活码文本

后端 API

当前信息屏设备流程使用以下端点:

  • POST /api/v1/signage/activate — 用于一次性激活码交换的公开端点
  • GET /api/v1/signage/device — 需认证的设备端点,返回当前教室的信息屏配置
  • GET /api/v1/signage/device/schedule?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD — 需认证的设备端点,返回绑定教室的课程表
  • POST /api/v1/signage/device/check-update — 需认证的设备端点,上报当前版本并检查 APK 更新

APK 下载认证

通过 /media/signageapk/ 提供的 APK 文件受 Nginx auth_request 保护。在提供每个文件之前,Nginx 会向 GET /api/v1/signage/apk-auth 发送内部子请求以验证设备 Bearer 令牌。未认证的请求会收到 401;该端点本身为 internal,无法从外部访问。

管理端端点

  • POST /api/v1/signage/classrooms/{id}/activation-code — 生成二维码绑定载荷
  • PATCH /api/v1/signage/classrooms/{id}/config — 更新信息屏配置
  • POST /api/v1/signage/classrooms/{id}/revoke-token — 吊销当前设备令牌
  • GET /api/v1/signage/classrooms/{id} — 仅限管理员的信息屏设备配置查询
  • GET /api/v1/signage/classrooms/{id}/schedule — 仅限管理员的教室课程表查询

信息屏设备数据模型

设备由 apps.signage.models.SignageDevice 表示,与 Classroom 保持一对一绑定关系。

重要字段:

  • 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

重要说明

后端仅存储令牌哈希和激活码哈希。Android 设备在激活后本地存储明文设备令牌。

重要行为变更

在阅读较早的文档或升级现有信息屏部署时,以下变更尤为重要:

  • 信息屏运行时为原生 Kotlin,而非 WebView
  • Android 信息屏不再使用硬编码的教室 ID。
  • Android 信息屏不再使用匿名的教室维度信息屏 API。
  • /api/v1/signage/classrooms/{id}/schedule/config 为管理端端点,信息屏设备不应直接调用。
  • 信息屏应用通过 Authorization: Bearer ... 请求头中的设备令牌进行认证。
  • 管理端二维码生成使用一次性激活码。
  • 吊销信息屏令牌会立即失效绑定的设备。
  • 当前信息屏应用不执行签到、人脸识别或 face-service 上传。

构建与运行

前提条件

  • Android Studio,或 Android SDK + 命令行工具
  • 当前 Android Gradle Plugin 所需的兼容 JDK

本项目使用 Gradle Version Catalog,详见 signage/gradle/libs.versions.toml

构建(Windows)

在仓库根目录下:

powershell
cd signage
.\gradlew.bat :app:assembleDebug

构建(Linux)

在仓库根目录下:

bash
cd signage
./gradlew :app:assembleDebug

TIP

如果遇到权限错误,请先运行 chmod +x ./gradlew

调试版 APK 通常生成在以下目录:

signage/app/build/outputs/apk/debug/

安装到设备

adb 可用:

bash
adb install -r signage/app/build/outputs/apk/debug/app-debug.apk

运维注意事项

  • BuildConfig.SIGNAGE_BASE_URL 是设备绑定前使用的默认 API 基础 URL。
  • 应用可能从扫描的二维码载荷中获取不同的 base_url 并在本地持久化,供后续请求使用。
  • 初始设备绑定需要 android.permission.CAMERA 权限。
  • 后端通信需要 android.permission.INTERNET 权限。
  • 当前清单中 android:allowBackup 已禁用。
  • 当前清单中 android:usesCleartextTraffic 仍为启用状态,以便于本地开发。生产部署应使用 HTTPS,并评估是否可以禁用明文流量。