add registration page with nickname/phone fields and fix lateinit crashes

This commit is contained in:
2026-08-31 15:06:43 +08:00
parent f2042e638f
commit 4ae77de307
5 changed files with 215 additions and 14 deletions

View File

@@ -115,6 +115,12 @@ class MainActivity : Activity() {
private var authUsernameInput: EditText? = null
private var authPasswordInput: EditText? = null
private var authStatusText: TextView? = null
private var regUsernameInput: EditText? = null
private var regPasswordInput: EditText? = null
private var regNicknameInput: EditText? = null
private var regPhoneInput: EditText? = null
private var regStatusText: TextView? = null
private var showingRegisterPage = false
private var myContentList: LinearLayout? = null
private var sentenceBoundaryRequestGeneration = 0
private var catalogVideos: List<TrainingVideoSummary> = emptyList()
@@ -320,7 +326,13 @@ class MainActivity : Activity() {
}
private fun setRootView() {
setContentView(if (isLoggedIn()) createContentView() else createAuthView())
setContentView(
when {
isLoggedIn() -> createContentView()
showingRegisterPage -> createRegisterView()
else -> createAuthView()
}
)
if (isLoggedIn()) {
renderCatalog()
refreshCurrentUi()
@@ -375,13 +387,137 @@ class MainActivity : Activity() {
})
addView(Button(this@MainActivity).apply {
text = "注册"
setOnClickListener { authenticate(register = true) }
setOnClickListener {
showingRegisterPage = true
setRootView()
}
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 44.dp).withMargins(0, 0, 0, 0)
})
addView(authStatusText)
}
}
private fun createRegisterView(): View {
return ScrollView(this).apply {
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
setBackgroundColor(COLOR_BACKGROUND)
addView(LinearLayout(this@MainActivity).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER_HORIZONTAL
setPadding(28.dp, 60.dp, 28.dp, 28.dp)
addView(TextView(this@MainActivity).apply {
text = "注册账号"
textSize = 28f
typeface = Typeface.DEFAULT_BOLD
setTextColor(Color.WHITE)
})
addView(TextView(this@MainActivity).apply {
text = "填写以下信息完成注册"
textSize = 14f
setTextColor(COLOR_TEXT_MUTED)
setPadding(0, 6.dp, 0, 24.dp)
})
addView(fieldLabel("用户名"))
regUsernameInput = EditText(this@MainActivity).apply {
hint = "3-50 位字母、数字或下划线"
setSingleLine(true)
}
addView(regUsernameInput)
addView(fieldLabel("密码"))
regPasswordInput = EditText(this@MainActivity).apply {
hint = "至少 8 位"
inputType = android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
setSingleLine(true)
}
addView(regPasswordInput)
addView(fieldLabel("姓名"))
regNicknameInput = EditText(this@MainActivity).apply {
hint = "你的真实姓名或昵称"
setSingleLine(true)
}
addView(regNicknameInput)
addView(fieldLabel("手机号"))
regPhoneInput = EditText(this@MainActivity).apply {
hint = "选填"
inputType = android.text.InputType.TYPE_CLASS_PHONE
setSingleLine(true)
}
addView(regPhoneInput)
regStatusText = TextView(this@MainActivity).apply {
setTextColor(COLOR_ACCENT_LIGHT)
textSize = 13f
setPadding(0, 14.dp, 0, 0)
}
addView(Button(this@MainActivity).apply {
text = "注册"
background = rounded(COLOR_ACCENT, 8f)
setTextColor(Color.WHITE)
setOnClickListener { submitRegistration() }
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 48.dp).withMargins(0, 20.dp, 0, 8.dp)
})
addView(Button(this@MainActivity).apply {
text = "返回登录"
setOnClickListener {
showingRegisterPage = false
regStatusText = null
setRootView()
}
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 44.dp).withMargins(0, 0, 0, 0)
})
addView(regStatusText)
})
}
}
private fun fieldLabel(label: String): TextView {
return TextView(this).apply {
text = label
textSize = 13f
setTextColor(COLOR_TEXT_MUTED)
setPadding(0, 12.dp, 0, 4.dp)
}
}
private fun submitRegistration() {
val username = regUsernameInput?.text?.toString()?.trim().orEmpty()
val password = regPasswordInput?.text?.toString().orEmpty()
val nickname = regNicknameInput?.text?.toString()?.trim().orEmpty()
val phone = regPhoneInput?.text?.toString()?.trim().orEmpty()
if (username.length < 3) {
regStatusText?.text = "用户名至少 3 个字符"
return
}
if (password.length < 8) {
regStatusText?.text = "密码至少 8 位"
return
}
if (nickname.isBlank()) {
regStatusText?.text = "请填写姓名"
return
}
regStatusText?.text = "注册中..."
sdk.userApi.register(username, password, nickname, phone) { result ->
result.fold(
onSuccess = { session ->
showingRegisterPage = false
saveAuthSession(session)
},
onFailure = { error ->
regStatusText?.text = "注册失败:${error.message}"
},
)
}
}
private fun authenticate(register: Boolean) {
val username = authUsernameInput?.text?.toString()?.trim().orEmpty()
val password = authPasswordInput?.text?.toString().orEmpty()
@@ -392,7 +528,7 @@ class MainActivity : Activity() {
onFailure = { error -> authStatusText?.text = "失败:${error.message}" },
)
}
if (register) sdk.userApi.register(username, password, callback)
if (register) sdk.userApi.register(username, password, onComplete = callback)
else sdk.userApi.login(username, password, callback)
}
@@ -1473,6 +1609,7 @@ class MainActivity : Activity() {
}
override fun onMediaChanged(item: TrainingMediaItem?) {
if (!::lessonTitleText.isInitialized) return
lessonTitleText.text = item?.title ?: "未选择课程"
currentSentenceCount = item?.sentences?.size ?: 0
}
@@ -1482,6 +1619,7 @@ class MainActivity : Activity() {
}
override fun onGesture(event: GestureEvent) {
if (!::statusText.isInitialized) return
statusText.text = when (event.kind) {
GestureKind.SINGLE_TAP -> "播放状态已切换"
GestureKind.SWIPE_LEFT -> "已跳到上一句"
@@ -1491,12 +1629,18 @@ class MainActivity : Activity() {
}
override fun onPlayerError(error: Throwable) {
if (!::statusText.isInitialized) return
statusText.text = "播放失败:${error.message.orEmpty()}"
}
}
}
private fun applyPlaybackSnapshot(snapshot: PlaybackSnapshot) {
if (!::timeText.isInitialized || !::progressBar.isInitialized ||
!::speedText.isInitialized || !::statusText.isInitialized
) {
return
}
if (!progressBarDragging) {
timeText.text = "${formatTime(snapshot.positionMs)} / ${formatTime(snapshot.durationMs)}"
progressBar.progress = playbackProgress(snapshot)
@@ -1514,6 +1658,11 @@ class MainActivity : Activity() {
}
private fun applySentence(sentence: SentenceBoundary?) {
if (!::sentenceMetaText.isInitialized || !::sentenceText.isInitialized ||
!::lessonTitleText.isInitialized
) {
return
}
if (sentence == null) {
sentenceMetaText.text = "暂无句子边界"
sentenceText.text = lessonTitleText.text
@@ -1606,6 +1755,11 @@ class MainActivity : Activity() {
}
private fun createCourseSection(): View {
if (!::catalogList.isInitialized) {
catalogList = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
}
}
return ScrollView(this).apply {
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)
addView(LinearLayout(this@MainActivity).apply {