software copyright

This commit is contained in:
jiahong
2026-03-22 15:24:40 +08:00
parent e303bb868a
commit 60f336e345
155 changed files with 127262 additions and 0 deletions
@@ -0,0 +1,414 @@
/**
* 自然写互动课堂电视端应用软件 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, "主界面资源已释放")
}
}