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.SeekBar
|
||||||
import android.widget.Switch
|
import android.widget.Switch
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import cn.learningpad.oraltrainer.sdk.GestureEvent
|
import cn.learningpad.oraltrainer.sdk.GestureEvent
|
||||||
import cn.learningpad.oraltrainer.sdk.GestureKind
|
import cn.learningpad.oraltrainer.sdk.GestureKind
|
||||||
import cn.learningpad.oraltrainer.sdk.AuthSession
|
import cn.learningpad.oraltrainer.sdk.AuthSession
|
||||||
@@ -127,6 +128,7 @@ class MainActivity : Activity() {
|
|||||||
private val courseEnrollment = mutableMapOf<String, Boolean>()
|
private val courseEnrollment = mutableMapOf<String, Boolean>()
|
||||||
private var catalogStatusTextValue = "正在同步"
|
private var catalogStatusTextValue = "正在同步"
|
||||||
private var activeModule = Module.TRAIN
|
private var activeModule = Module.TRAIN
|
||||||
|
private var currentCourse: TrainingVideoSummary? = null
|
||||||
private var continuousPlaybackEnabled = false
|
private var continuousPlaybackEnabled = false
|
||||||
private var testSubtitleVisible = false
|
private var testSubtitleVisible = false
|
||||||
private var landscapeSubtitleVisible = true
|
private var landscapeSubtitleVisible = true
|
||||||
@@ -550,6 +552,7 @@ class MainActivity : Activity() {
|
|||||||
authToken = ""
|
authToken = ""
|
||||||
authNickname = ""
|
authNickname = ""
|
||||||
sdk.config.userToken = null
|
sdk.config.userToken = null
|
||||||
|
currentCourse = null
|
||||||
controller.pause()
|
controller.pause()
|
||||||
activeModule = Module.TRAIN
|
activeModule = Module.TRAIN
|
||||||
setRootView()
|
setRootView()
|
||||||
@@ -612,13 +615,17 @@ class MainActivity : Activity() {
|
|||||||
}
|
}
|
||||||
layoutParams = LinearLayout.LayoutParams(0, 44.dp, 1f).withMargins(0, 0, 8.dp, 0)
|
layoutParams = LinearLayout.LayoutParams(0, 44.dp, 1f).withMargins(0, 0, 8.dp, 0)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
if (activeModule == module) {
|
if (activeModule == module) {
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
if (module == Module.COURSE || module == Module.MINE) {
|
if (module != Module.COURSE && currentCourse == null) {
|
||||||
loadUserData()
|
Toast.makeText(this@MainActivity, "请先选择课程", Toast.LENGTH_SHORT).show()
|
||||||
}
|
return@setOnClickListener
|
||||||
activeModule = module
|
}
|
||||||
|
if (module == Module.COURSE || module == Module.MINE) {
|
||||||
|
loadUserData()
|
||||||
|
}
|
||||||
|
activeModule = module
|
||||||
controller.setContinuousPlayback(
|
controller.setContinuousPlayback(
|
||||||
if (activeModule == Module.TRAIN) continuousPlaybackEnabled else false
|
if (activeModule == Module.TRAIN) continuousPlaybackEnabled else false
|
||||||
)
|
)
|
||||||
@@ -948,7 +955,7 @@ class MainActivity : Activity() {
|
|||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
setPadding(0, 10.dp, 0, 4.dp)
|
setPadding(0, 10.dp, 0, 4.dp)
|
||||||
}
|
}
|
||||||
|
// Fresh instances each time — safe to addView.
|
||||||
val header = LinearLayout(this).apply {
|
val header = LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
gravity = Gravity.CENTER_VERTICAL
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
@@ -1757,28 +1764,35 @@ class MainActivity : Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createCourseSection(): View {
|
private fun createCourseSection(): View {
|
||||||
if (!::catalogList.isInitialized) {
|
catalogStatusText = TextView(this).apply {
|
||||||
catalogList = LinearLayout(this).apply {
|
setTextColor(COLOR_TEXT_MUTED)
|
||||||
orientation = LinearLayout.VERTICAL
|
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 {
|
return ScrollView(this).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)
|
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)
|
||||||
addView(LinearLayout(this@MainActivity).apply {
|
addView(LinearLayout(this@MainActivity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
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 {
|
addView(TextView(this@MainActivity).apply {
|
||||||
text = "课程首页"
|
text = "课程首页"
|
||||||
textSize = 26f
|
textSize = 26f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
setTextColor(Color.WHITE)
|
setTextColor(Color.WHITE)
|
||||||
|
setPadding(16.dp, 0.dp, 16.dp, 0.dp)
|
||||||
})
|
})
|
||||||
addView(TextView(this@MainActivity).apply {
|
addView(TextView(this@MainActivity).apply {
|
||||||
text = "选择一门课程开始训练"
|
text = "选择一门课程开始训练"
|
||||||
textSize = 13f
|
textSize = 13f
|
||||||
setTextColor(COLOR_TEXT_MUTED)
|
setTextColor(COLOR_TEXT_MUTED)
|
||||||
setPadding(0, 4.dp, 0, 16.dp)
|
setPadding(16.dp, 4.dp, 16.dp, 8.dp)
|
||||||
})
|
})
|
||||||
|
addView(catalogStatusText)
|
||||||
addView(catalogList)
|
addView(catalogList)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1818,15 +1832,35 @@ class MainActivity : Activity() {
|
|||||||
private fun refreshMyContent() {
|
private fun refreshMyContent() {
|
||||||
val list = myContentList ?: return
|
val list = myContentList ?: return
|
||||||
list.removeAllViews()
|
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("加载中..."))
|
list.addView(infoCard("加载中..."))
|
||||||
sdk.userApi.results(authToken) { result ->
|
sdk.userApi.results(authToken) { result ->
|
||||||
list.removeAllViews()
|
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(
|
result.fold(
|
||||||
onSuccess = { rows ->
|
onSuccess = { rows ->
|
||||||
if (rows.isEmpty()) list.addView(infoCard("还没有测试记录"))
|
val filtered = rows.filter { it.videoHash == course.videoHash }
|
||||||
rows.forEach { row ->
|
if (filtered.isEmpty()) list.addView(infoCard("该课程还没有测试记录"))
|
||||||
|
filtered.forEach { row ->
|
||||||
list.addView(infoCard(
|
list.addView(infoCard(
|
||||||
"${row.courseTitle} · 第 ${row.sentenceIndex + 1} 句\n" +
|
"第 ${row.sentenceIndex + 1} 句\n" +
|
||||||
"得分 ${row.overallScore.toInt()} · ${if (row.passed) "通过" else "未通过"}"
|
"得分 ${row.overallScore.toInt()} · ${if (row.passed) "通过" else "未通过"}"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -1838,10 +1872,11 @@ class MainActivity : Activity() {
|
|||||||
list.addView(titleDivider("我的配音分享"))
|
list.addView(titleDivider("我的配音分享"))
|
||||||
shareResult.fold(
|
shareResult.fold(
|
||||||
onSuccess = { shares ->
|
onSuccess = { shares ->
|
||||||
if (shares.isEmpty()) list.addView(infoCard("还没有分享配音"))
|
val filtered = shares.filter { it.videoHash == course.videoHash }
|
||||||
shares.forEach { row ->
|
if (filtered.isEmpty()) list.addView(infoCard("该课程还没有分享配音"))
|
||||||
|
filtered.forEach { row ->
|
||||||
list.addView(infoCard(
|
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
|
return
|
||||||
}
|
}
|
||||||
catalogList.removeAllViews()
|
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 ->
|
catalogVideos.forEach { video ->
|
||||||
val enrolled = courseEnrollment[video.videoHash] == true
|
val enrolled = courseEnrollment[video.videoHash] == true
|
||||||
catalogList.addView(
|
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) {
|
private fun enrollCourse(video: TrainingVideoSummary) {
|
||||||
sdk.userApi.enroll(authToken, video.videoHash) { result ->
|
sdk.userApi.enroll(authToken, video.videoHash) { result ->
|
||||||
result.fold(
|
result.fold(
|
||||||
|
|||||||
Reference in New Issue
Block a user