Files
system-design/software-copyright/07-writech-app-tv/ui/MainFragment.kt
T
2026-03-22 15:24:40 +08:00

415 lines
15 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 自然写互动课堂电视端应用软件 V1.0
* Leanback主界面Fragment - Android TV主界面导航
*
* 功能说明:
* 1. Leanback BrowseSupportFragment主界面布局
* 2. D-Pad遥控器焦点导航适配(方向键/确认键/返回键)
* 3. 多功能区域展示(课堂笔迹、互动答题、学情报告、设置)
* 4. 课堂状态实时显示(当前课堂信息、在线学生数)
* 5. 语音操控集成(Android TV语音搜索)
* 6. 网关连接状态指示
* 7. 自动全屏沉浸式模式
*/
package com.writech.tv.ui
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import java.text.SimpleDateFormat
import java.util.*
/**
* TV端主界面数据模型 - 功能卡片
*/
data class FunctionCard(
val id: String, // 卡片唯一标识
val title: String, // 标题
val description: String, // 描述
val iconRes: Int, // 图标资源ID
val category: String, // 所属分类
val action: String // 点击动作标识
)
/**
* 课堂状态信息
*/
data class ClassroomStatus(
var isActive: Boolean = false, // 是否有进行中的课堂
var classId: String = "", // 课堂ID
var className: String = "", // 课堂名称
var teacherName: String = "", // 授课教师
var onlineStudentCount: Int = 0, // 在线学生数
var totalStudentCount: Int = 0, // 总学生数
var startTime: Long = 0, // 课堂开始时间
var currentSubject: String = "" // 当前科目
)
/**
* TV端Leanback主界面Fragment
* 采用Android TV Leanback库的BrowseSupportFragment风格
* 适配遥控器D-Pad焦点导航操作
*/
class MainFragment {
companion object {
private const val TAG = "MainFragment"
// 功能分类ID
private const val CATEGORY_CLASSROOM = "classroom"
private const val CATEGORY_INTERACTIVE = "interactive"
private const val CATEGORY_REPORT = "report"
private const val CATEGORY_SETTINGS = "settings"
}
/** 当前课堂状态 */
private val classroomStatus = ClassroomStatus()
/** 功能卡片列表(按分类组织) */
private val functionCards = mutableMapOf<String, MutableList<FunctionCard>>()
/** 主线程Handler */
private val handler = Handler(Looper.getMainLooper())
/** 课堂计时器 */
private var classroomTimer: Timer? = null
/** 日期格式化器 */
private val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA)
/**
* 初始化界面
* 配置Leanback样式、加载功能卡片、设置焦点导航
*/
fun initialize() {
// 配置Leanback主题色
// brandColor = Color.parseColor("#1976D2")
// searchAffordanceColor = Color.parseColor("#2196F3")
// 加载功能卡片数据
loadFunctionCards()
// 设置搜索回调(语音搜索)
setupSearch()
// 设置全屏沉浸式模式
setupImmersiveMode()
Log.i(TAG, "主界面初始化完成")
}
/**
* 加载功能卡片列表
* 按分类组织:课堂展示、互动答题、学情报告、系统设置
*/
private fun loadFunctionCards() {
// 课堂展示功能
val classroomCards = mutableListOf(
FunctionCard(
id = "stroke_display",
title = "全班笔迹实时展示",
description = "大屏展示全班学生实时书写笔迹",
iconRes = 0, // R.drawable.ic_stroke_display
category = CATEGORY_CLASSROOM,
action = "open_stroke_display"
),
FunctionCard(
id = "multi_compare",
title = "多学生同屏对比",
description = "选择学生笔迹并排对比展示",
iconRes = 0,
category = CATEGORY_CLASSROOM,
action = "open_multi_compare"
),
FunctionCard(
id = "copybook_display",
title = "字帖临摹展示",
description = "放大范字与学生实时书写对比",
iconRes = 0,
category = CATEGORY_CLASSROOM,
action = "open_copybook"
),
FunctionCard(
id = "stroke_replay",
title = "笔迹回放",
description = "回放学生书写过程(支持变速)",
iconRes = 0,
category = CATEGORY_CLASSROOM,
action = "open_replay"
)
)
// 课堂互动功能
val interactiveCards = mutableListOf(
FunctionCard(
id = "quiz_display",
title = "答题结果展示",
description = "大屏展示课堂互动答题统计",
iconRes = 0,
category = CATEGORY_INTERACTIVE,
action = "open_quiz_display"
),
FunctionCard(
id = "random_pick",
title = "随机点名",
description = "随机抽取学生进行展示",
iconRes = 0,
category = CATEGORY_INTERACTIVE,
action = "open_random_pick"
),
FunctionCard(
id = "group_display",
title = "分组展示",
description = "按小组展示学生作品",
iconRes = 0,
category = CATEGORY_INTERACTIVE,
action = "open_group_display"
)
)
// 学情报告功能
val reportCards = mutableListOf(
FunctionCard(
id = "class_report",
title = "班级学情概览",
description = "班级整体学情数据大屏展示",
iconRes = 0,
category = CATEGORY_REPORT,
action = "open_class_report"
),
FunctionCard(
id = "student_report",
title = "学生学情详情",
description = "单个学生学情画像详细展示",
iconRes = 0,
category = CATEGORY_REPORT,
action = "open_student_report"
),
FunctionCard(
id = "growth_chart",
title = "书写成长轨迹",
description = "学生书写能力变化趋势图",
iconRes = 0,
category = CATEGORY_REPORT,
action = "open_growth_chart"
)
)
// 系统设置功能
val settingsCards = mutableListOf(
FunctionCard(
id = "gateway_settings",
title = "网关连接",
description = "搜索并绑定教室网关设备",
iconRes = 0,
category = CATEGORY_SETTINGS,
action = "open_gateway_settings"
),
FunctionCard(
id = "display_settings",
title = "显示设置",
description = "分辨率、字体大小、背景色调整",
iconRes = 0,
category = CATEGORY_SETTINGS,
action = "open_display_settings"
),
FunctionCard(
id = "network_settings",
title = "网络设置",
description = "WiFi连接、云平台地址配置",
iconRes = 0,
category = CATEGORY_SETTINGS,
action = "open_network_settings"
),
FunctionCard(
id = "about",
title = "关于",
description = "版本信息、设备ID、软件许可",
iconRes = 0,
category = CATEGORY_SETTINGS,
action = "open_about"
)
)
functionCards[CATEGORY_CLASSROOM] = classroomCards
functionCards[CATEGORY_INTERACTIVE] = interactiveCards
functionCards[CATEGORY_REPORT] = reportCards
functionCards[CATEGORY_SETTINGS] = settingsCards
Log.i(TAG, "功能卡片加载完成,共${functionCards.values.sumOf { it.size }}个")
}
/**
* 处理功能卡片点击事件
* 根据action标识跳转到对应的功能Fragment
*/
fun onCardSelected(card: FunctionCard) {
Log.i(TAG, "选中功能: ${card.title} -> ${card.action}")
when (card.action) {
"open_stroke_display" -> navigateToStrokeDisplay()
"open_multi_compare" -> navigateToMultiCompare()
"open_copybook" -> navigateToCopybookDisplay()
"open_replay" -> navigateToReplay()
"open_quiz_display" -> navigateToQuizDisplay()
"open_random_pick" -> performRandomPick()
"open_group_display" -> navigateToGroupDisplay()
"open_class_report" -> navigateToClassReport()
"open_student_report" -> navigateToStudentReport()
"open_growth_chart" -> navigateToGrowthChart()
"open_gateway_settings" -> navigateToGatewaySettings()
"open_display_settings" -> navigateToDisplaySettings()
"open_network_settings" -> navigateToNetworkSettings()
"open_about" -> navigateToAbout()
else -> Log.w(TAG, "未知操作: ${card.action}")
}
}
/** 设置语音搜索(Android TV Voice Search */
private fun setupSearch() {
// setOnSearchClickedListener { openSearchFragment() }
Log.i(TAG, "语音搜索配置完成")
}
/** 设置全屏沉浸式模式 */
private fun setupImmersiveMode() {
// activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE) // 防截屏
Log.i(TAG, "沉浸式模式已启用")
}
/**
* 处理遥控器按键事件
* 适配D-Pad方向键、确认键、返回键、菜单键
*/
fun onKeyEvent(keyCode: Int, event: KeyEvent): Boolean {
return when (keyCode) {
KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER -> {
// 确认键:选中当前焦点项
Log.d(TAG, "遥控器确认键按下")
false // 交给焦点系统处理
}
KeyEvent.KEYCODE_MENU -> {
// 菜单键:显示快捷操作面板
showQuickActions()
true
}
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
// 播放/暂停键:控制笔迹回放
toggleReplayPause()
true
}
else -> false
}
}
/** 显示快捷操作面板 */
private fun showQuickActions() {
Log.i(TAG, "显示快捷操作面板")
}
/** 切换回放暂停/继续 */
private fun toggleReplayPause() {
Log.i(TAG, "切换回放状态")
}
/* ========== 课堂状态管理 ========== */
/** 更新课堂状态 */
fun updateClassroomStatus(status: ClassroomStatus) {
classroomStatus.isActive = status.isActive
classroomStatus.classId = status.classId
classroomStatus.className = status.className
classroomStatus.teacherName = status.teacherName
classroomStatus.onlineStudentCount = status.onlineStudentCount
classroomStatus.totalStudentCount = status.totalStudentCount
classroomStatus.startTime = status.startTime
classroomStatus.currentSubject = status.currentSubject
if (status.isActive) {
startClassroomTimer()
} else {
stopClassroomTimer()
}
// 更新Header显示
updateHeaderInfo()
}
/** 启动课堂计时器(实时显示课堂进行时长) */
private fun startClassroomTimer() {
stopClassroomTimer()
classroomTimer = Timer("classroom-timer")
classroomTimer?.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
val elapsed = System.currentTimeMillis() - classroomStatus.startTime
val minutes = (elapsed / 60000).toInt()
val seconds = ((elapsed % 60000) / 1000).toInt()
val timeStr = String.format("%02d:%02d", minutes, seconds)
handler.post {
// 更新课堂时长显示
Log.d(TAG, "课堂进行: $timeStr")
}
}
}, 0, 1000)
}
/** 停止课堂计时器 */
private fun stopClassroomTimer() {
classroomTimer?.cancel()
classroomTimer = null
}
/** 更新顶部标题栏信息 */
private fun updateHeaderInfo() {
val title = if (classroomStatus.isActive) {
"${classroomStatus.className} - ${classroomStatus.currentSubject}" +
" (${classroomStatus.onlineStudentCount}/${classroomStatus.totalStudentCount}人在线)"
} else {
"自然写互动课堂"
}
// 设置标题
Log.i(TAG, "更新标题: $title")
}
/** 执行随机点名 */
private fun performRandomPick() {
if (!classroomStatus.isActive) {
Log.w(TAG, "当前无进行中的课堂,无法随机点名")
return
}
// 从在线学生列表中随机抽取
Log.i(TAG, "执行随机点名")
}
/* ========== 导航方法 ========== */
private fun navigateToStrokeDisplay() { Log.i(TAG, "跳转: 全班笔迹展示") }
private fun navigateToMultiCompare() { Log.i(TAG, "跳转: 多学生对比") }
private fun navigateToCopybookDisplay() { Log.i(TAG, "跳转: 字帖临摹") }
private fun navigateToReplay() { Log.i(TAG, "跳转: 笔迹回放") }
private fun navigateToQuizDisplay() { Log.i(TAG, "跳转: 答题展示") }
private fun navigateToGroupDisplay() { Log.i(TAG, "跳转: 分组展示") }
private fun navigateToClassReport() { Log.i(TAG, "跳转: 班级学情") }
private fun navigateToStudentReport() { Log.i(TAG, "跳转: 学生学情") }
private fun navigateToGrowthChart() { Log.i(TAG, "跳转: 成长轨迹") }
private fun navigateToGatewaySettings() { Log.i(TAG, "跳转: 网关设置") }
private fun navigateToDisplaySettings() { Log.i(TAG, "跳转: 显示设置") }
private fun navigateToNetworkSettings() { Log.i(TAG, "跳转: 网络设置") }
private fun navigateToAbout() { Log.i(TAG, "跳转: 关于") }
/** 释放资源 */
fun release() {
stopClassroomTimer()
functionCards.clear()
Log.i(TAG, "主界面资源已释放")
}
}