教室数字信息屏
作者: 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 WebView 和 assets/index.html 作为生产运行时路径。这已不再是当前的实现方式。
signage/web/ 包可能仍作为历史代码存在于仓库中,但当前应用不通过 WebView 渲染 UI。
工作原理
启动流程
启动时,MainActivity 依次执行:
- 启用沉浸式全屏模式。
- 初始化信息屏 UI 和时间轴组件。
- 检查加密本地存储中是否已存在设备令牌。
- 若不存在令牌,打开相机流程等待扫描设置二维码。
- 若存在令牌,发起请求:
GET /api/v1/signage/deviceGET /api/v1/signage/device/schedule
- 若后端返回
401或403,清除本地设备令牌并强制重新绑定。
设备绑定流程
当前信息屏应用支持激活码绑定和二维码载荷中的直接令牌绑定两种方式。
- 管理端调用方向后端请求二维码绑定数据。
- 后端返回包含
classroom_id、activation_code和base_url的激活码载荷。 - Android 信息屏扫描二维码载荷。
- Android 应用加载或创建本地
device_code,当载荷包含activation_code时调用POST /api/v1/signage/activate。 - 应用将设备令牌存储在由 Android Keystore 支持的
EncryptedSharedPreferences中。
当前应用支持的二维码载荷格式:
- 包含
activation_code以及可选的classroom_id和base_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_codedevice_labelsignage_titlesignage_subtitlesignage_configdevice_token_hashtoken_issued_atactivated_atlast_seen_atactivation_code_hashactivation_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)
在仓库根目录下:
cd signage
.\gradlew.bat :app:assembleDebug构建(Linux)
在仓库根目录下:
cd signage
./gradlew :app:assembleDebugTIP
如果遇到权限错误,请先运行 chmod +x ./gradlew。
调试版 APK 通常生成在以下目录:
signage/app/build/outputs/apk/debug/
安装到设备
若 adb 可用:
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,并评估是否可以禁用明文流量。