redesign course page: select course to enter training, guard tabs without course
This commit is contained in:
@@ -42,6 +42,7 @@ import android.widget.ScrollView
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Switch
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import cn.learningpad.oraltrainer.sdk.GestureEvent
|
||||
import cn.learningpad.oraltrainer.sdk.GestureKind
|
||||
import cn.learningpad.oraltrainer.sdk.AuthSession
|
||||
@@ -127,6 +128,7 @@ class MainActivity : Activity() {
|
||||
private val courseEnrollment = mutableMapOf<String, Boolean>()
|
||||
private var catalogStatusTextValue = "正在同步"
|
||||
private var activeModule = Module.TRAIN
|
||||
private var currentCourse: TrainingVideoSummary? = null
|
||||
private var continuousPlaybackEnabled = false
|
||||
private var testSubtitleVisible = false
|
||||
private var landscapeSubtitleVisible = true
|
||||
@@ -550,6 +552,7 @@ class MainActivity : Activity() {
|
||||
authToken = ""
|
||||
authNickname = ""
|
||||
sdk.config.userToken = null
|
||||
currentCourse = null
|
||||
controller.pause()
|
||||
activeModule = Module.TRAIN
|
||||
setRootView()
|
||||
@@ -615,6 +618,10 @@ class MainActivity : Activity() {
|
||||
if (activeModule == module) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (module != Module.COURSE && currentCourse == null) {
|
||||
Toast.makeText(this@MainActivity, "请先选择课程", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (module == Module.COURSE || module == Module.MINE) {
|
||||
loadUserData()
|
||||
}
|
||||
@@ -948,7 +955,7 @@ class MainActivity : Activity() {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
setPadding(0, 10.dp, 0, 4.dp)
|
||||
}
|
||||
|
||||
// Fresh instances each time — safe to addView.
|
||||
val header = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
@@ -1757,28 +1764,35 @@ class MainActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun createCourseSection(): View {
|
||||
if (!::catalogList.isInitialized) {
|
||||
catalogStatusText = TextView(this).apply {
|
||||
setTextColor(COLOR_TEXT_MUTED)
|
||||
textSize = 13f
|
||||
text = catalogStatusTextValue
|
||||
setPadding(16.dp, 0.dp, 16.dp, 8.dp)
|
||||
}
|
||||
catalogList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
}
|
||||
setPadding(16.dp, 8.dp, 16.dp, 16.dp)
|
||||
}
|
||||
return ScrollView(this).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)
|
||||
addView(LinearLayout(this@MainActivity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(16.dp, 12.dp, 16.dp, 16.dp)
|
||||
setPadding(0.dp, 12.dp, 0.dp, 16.dp)
|
||||
addView(TextView(this@MainActivity).apply {
|
||||
text = "课程首页"
|
||||
textSize = 26f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setTextColor(Color.WHITE)
|
||||
setPadding(16.dp, 0.dp, 16.dp, 0.dp)
|
||||
})
|
||||
addView(TextView(this@MainActivity).apply {
|
||||
text = "选择一门课程开始训练"
|
||||
textSize = 13f
|
||||
setTextColor(COLOR_TEXT_MUTED)
|
||||
setPadding(0, 4.dp, 0, 16.dp)
|
||||
setPadding(16.dp, 4.dp, 16.dp, 8.dp)
|
||||
})
|
||||
addView(catalogStatusText)
|
||||
addView(catalogList)
|
||||
})
|
||||
}
|
||||
@@ -1818,15 +1832,35 @@ class MainActivity : Activity() {
|
||||
private fun refreshMyContent() {
|
||||
val list = myContentList ?: return
|
||||
list.removeAllViews()
|
||||
val course = currentCourse
|
||||
if (course == null) {
|
||||
list.addView(infoCard("请先在「课程」中选择一门课程"))
|
||||
return
|
||||
}
|
||||
list.addView(TextView(this).apply {
|
||||
text = "当前课程:${course.title}"
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 14f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setPadding(0, 0, 0, 8.dp)
|
||||
})
|
||||
list.addView(infoCard("加载中..."))
|
||||
sdk.userApi.results(authToken) { result ->
|
||||
list.removeAllViews()
|
||||
list.addView(TextView(this).apply {
|
||||
text = "当前课程:${course.title}"
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 14f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setPadding(0, 0, 0, 8.dp)
|
||||
})
|
||||
result.fold(
|
||||
onSuccess = { rows ->
|
||||
if (rows.isEmpty()) list.addView(infoCard("还没有测试记录"))
|
||||
rows.forEach { row ->
|
||||
val filtered = rows.filter { it.videoHash == course.videoHash }
|
||||
if (filtered.isEmpty()) list.addView(infoCard("该课程还没有测试记录"))
|
||||
filtered.forEach { row ->
|
||||
list.addView(infoCard(
|
||||
"${row.courseTitle} · 第 ${row.sentenceIndex + 1} 句\n" +
|
||||
"第 ${row.sentenceIndex + 1} 句\n" +
|
||||
"得分 ${row.overallScore.toInt()} · ${if (row.passed) "通过" else "未通过"}"
|
||||
))
|
||||
}
|
||||
@@ -1838,10 +1872,11 @@ class MainActivity : Activity() {
|
||||
list.addView(titleDivider("我的配音分享"))
|
||||
shareResult.fold(
|
||||
onSuccess = { shares ->
|
||||
if (shares.isEmpty()) list.addView(infoCard("还没有分享配音"))
|
||||
shares.forEach { row ->
|
||||
val filtered = shares.filter { it.videoHash == course.videoHash }
|
||||
if (filtered.isEmpty()) list.addView(infoCard("该课程还没有分享配音"))
|
||||
filtered.forEach { row ->
|
||||
list.addView(infoCard(
|
||||
"${row.title}\n${row.courseTitle} · ${row.segmentCount} 句 · 平均 ${row.averageScore.toInt()} 分"
|
||||
"${row.title}\n${row.segmentCount} 句 · 平均 ${row.averageScore.toInt()} 分"
|
||||
))
|
||||
}
|
||||
},
|
||||
@@ -1881,6 +1916,18 @@ class MainActivity : Activity() {
|
||||
return
|
||||
}
|
||||
catalogList.removeAllViews()
|
||||
if (activeModule == Module.COURSE) {
|
||||
if (catalogVideos.isEmpty()) {
|
||||
catalogList.addView(infoCard("暂无课程"))
|
||||
return
|
||||
}
|
||||
catalogVideos.forEach { video ->
|
||||
catalogList.addView(courseListCard(video) {
|
||||
selectCourse(video)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
catalogVideos.forEach { video ->
|
||||
val enrolled = courseEnrollment[video.videoHash] == true
|
||||
catalogList.addView(
|
||||
@@ -1899,6 +1946,59 @@ class MainActivity : Activity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun courseListCard(
|
||||
video: TrainingVideoSummary,
|
||||
onClick: () -> Unit,
|
||||
): View {
|
||||
val enrolled = courseEnrollment[video.videoHash] == true
|
||||
val isSelected = currentCourse?.videoHash == video.videoHash
|
||||
return LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
isClickable = true
|
||||
background = if (isSelected) {
|
||||
rounded(COLOR_SELECTED, 10f, COLOR_ACCENT)
|
||||
} else {
|
||||
rounded(COLOR_SURFACE, 10f, COLOR_BORDER)
|
||||
}
|
||||
setPadding(16.dp, 14.dp, 16.dp, 14.dp)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
).withMargins(0, 0, 0, 10.dp)
|
||||
setOnClickListener { onClick() }
|
||||
addView(TextView(this@MainActivity).apply {
|
||||
text = video.title
|
||||
setTextColor(if (isSelected) COLOR_TEXT_DARK else Color.WHITE)
|
||||
textSize = 16f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
maxLines = 2
|
||||
})
|
||||
addView(TextView(this@MainActivity).apply {
|
||||
text = buildString {
|
||||
append(if (enrolled) "已选修" else "未选修")
|
||||
append(" · ${video.sentenceCount} 句")
|
||||
if (isSelected) append(" · 当前课程")
|
||||
}
|
||||
setTextColor(if (isSelected) COLOR_ACCENT_DEEP else COLOR_TEXT_MUTED)
|
||||
textSize = 13f
|
||||
setPadding(0, 6.dp, 0, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectCourse(video: TrainingVideoSummary) {
|
||||
currentCourse = video
|
||||
if (courseEnrollment[video.videoHash] != true) {
|
||||
enrollCourse(video)
|
||||
}
|
||||
activeModule = Module.TRAIN
|
||||
landscapeSubtitleVisible = true
|
||||
setContentView(createContentView())
|
||||
renderCatalog()
|
||||
refreshCurrentUi()
|
||||
loadRemoteVideo(video)
|
||||
}
|
||||
|
||||
private fun enrollCourse(video: TrainingVideoSummary) {
|
||||
sdk.userApi.enroll(authToken, video.videoHash) { result ->
|
||||
result.fold(
|
||||
|
||||
Reference in New Issue
Block a user