relayout of train view
This commit is contained in:
@@ -13,7 +13,6 @@ internal const val ASSESSMENT_API_KEY =
|
||||
|
||||
internal const val SSO_LOGIN_URL = "https://d1kt.cn/login"
|
||||
|
||||
internal const val PICK_VIDEO_REQUEST = 1001
|
||||
internal const val RECORD_AUDIO_REQUEST = 1002
|
||||
internal const val PROGRESS_MAX = 1000
|
||||
|
||||
|
||||
@@ -167,14 +167,6 @@ internal class CourseCatalogController(
|
||||
}
|
||||
}
|
||||
|
||||
fun useCourseInPlace(video: TrainingVideoSummary) {
|
||||
// Keep the current module (e.g. Train) while syncing course selection.
|
||||
if (screenState.courseEnrollment[video.videoHash] != true) {
|
||||
enrollCourse(video)
|
||||
}
|
||||
loadRemoteVideo(video)
|
||||
}
|
||||
|
||||
fun loadRemoteVideo(video: TrainingVideoSummary) {
|
||||
screenState.currentCourse = video
|
||||
screenState.activeItemId = video.videoHash
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import cn.learningpad.oraltrainer.sdk.AuthSession
|
||||
import cn.learningpad.oraltrainer.sdk.GestureEvent
|
||||
@@ -17,8 +16,6 @@ import cn.learningpad.oraltrainer.sdk.OralTrainerListener
|
||||
import cn.learningpad.oraltrainer.sdk.OralTrainerSdk
|
||||
import cn.learningpad.oraltrainer.sdk.PlaybackSnapshot
|
||||
import cn.learningpad.oraltrainer.sdk.SentenceBoundary
|
||||
import cn.learningpad.oraltrainer.sdk.SentenceBoundaryApiCallback
|
||||
import cn.learningpad.oraltrainer.sdk.SentenceBoundaryApiResult
|
||||
import cn.learningpad.oraltrainer.sdk.TrainingMediaItem
|
||||
import cn.learningpad.oraltrainer.sdk.TrainingVideoSummary
|
||||
|
||||
@@ -173,53 +170,6 @@ class MainActivity : Activity() {
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode != PICK_VIDEO_REQUEST || resultCode != RESULT_OK) return
|
||||
val uri = data?.data ?: return
|
||||
val persistableFlags = data.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
if (persistableFlags != 0) {
|
||||
runCatching { contentResolver.takePersistableUriPermission(uri, persistableFlags) }
|
||||
}
|
||||
|
||||
val item = TrainingMediaItem(
|
||||
id = uri.toString(),
|
||||
title = uri.lastPathSegment ?: "本地视频",
|
||||
uri = uri,
|
||||
)
|
||||
screenState.activeItemId = item.id
|
||||
screenState.currentSentenceCount = 0
|
||||
controller.loadItem(item)
|
||||
if (bindings.hasPlayerChrome) {
|
||||
bindings.lessonTitleText.text = item.title
|
||||
bindings.sentenceMetaText.text = "句子边界分析中"
|
||||
bindings.sentenceText.text = "正在匹配服务端句子边界"
|
||||
bindings.statusText.text = "正在获取本地视频的句子边界..."
|
||||
}
|
||||
screenFactory.renderCatalog()
|
||||
|
||||
sdk.sentenceBoundaryApi.fetchForUri(uri, contentResolver, object : SentenceBoundaryApiCallback {
|
||||
override fun onSuccess(result: SentenceBoundaryApiResult) {
|
||||
if (screenState.activeItemId != item.id) return
|
||||
screenState.currentSentenceCount = result.sentences.size
|
||||
controller.loadItem(item.copy(sentences = result.sentences))
|
||||
if (bindings.hasPlayerChrome) {
|
||||
bindings.statusText.text = "句子边界已加载:${result.sentences.size} 句"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(error: Throwable) {
|
||||
if (screenState.activeItemId != item.id) return
|
||||
if (bindings.hasPlayerChrome) {
|
||||
bindings.statusText.text =
|
||||
"句子边界获取失败,使用默认快退/快进:${error.message.orEmpty()}"
|
||||
bindings.sentenceMetaText.text = "暂无句子边界"
|
||||
bindings.sentenceText.text = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
authCoordinator.reset()
|
||||
testSession.release()
|
||||
@@ -268,19 +218,6 @@ class MainActivity : Activity() {
|
||||
setRootView()
|
||||
}
|
||||
|
||||
private fun openLocalVideoPicker() {
|
||||
@Suppress("DEPRECATION")
|
||||
startActivityForResult(
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "video/*"
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
},
|
||||
PICK_VIDEO_REQUEST,
|
||||
)
|
||||
}
|
||||
|
||||
private fun switchModule(module: Module) {
|
||||
if (module != Module.TEST) {
|
||||
testSession.stopDubbingIfNeeded()
|
||||
@@ -393,10 +330,6 @@ class MainActivity : Activity() {
|
||||
private fun screenActions() = object : SampleScreenActions {
|
||||
override fun onModuleSelected(module: Module) = switchModule(module)
|
||||
|
||||
override fun onImportLocalVideo() = openLocalVideoPicker()
|
||||
|
||||
override fun onRefreshCatalog() = catalogController.loadCatalog()
|
||||
|
||||
override fun onSeekProgressPreview(progress: Int) {
|
||||
if (!bindings.hasPlayerChrome) return
|
||||
val duration = controller.snapshot().durationMs
|
||||
@@ -474,10 +407,6 @@ class MainActivity : Activity() {
|
||||
|
||||
override fun onRefreshCourses() = catalogController.refreshCourses()
|
||||
|
||||
override fun onTrainCatalogVideoClick(video: TrainingVideoSummary) {
|
||||
catalogController.useCourseInPlace(video)
|
||||
}
|
||||
|
||||
override fun onLogout() = logout()
|
||||
|
||||
override fun nickname(): String =
|
||||
|
||||
@@ -17,13 +17,10 @@ internal interface SentenceExplainActions {
|
||||
|
||||
internal interface TrainScreenActions :
|
||||
SentenceExplainActions {
|
||||
fun onImportLocalVideo()
|
||||
fun onRefreshCatalog()
|
||||
fun onSeekProgressPreview(progress: Int)
|
||||
fun onSeekStart()
|
||||
fun onSeekStop(progress: Int)
|
||||
fun onContinuousPlaybackChanged(enabled: Boolean)
|
||||
fun onTrainCatalogVideoClick(video: TrainingVideoSummary)
|
||||
}
|
||||
|
||||
internal interface TestScreenActions :
|
||||
|
||||
@@ -33,11 +33,7 @@ internal class SampleScreenFactory(
|
||||
private val courseScreen = CourseScreen(activity, bindings, screenState, widgets, actions)
|
||||
private val trainScreen = TrainScreen(
|
||||
activity = activity,
|
||||
bindings = bindings,
|
||||
screenState = screenState,
|
||||
widgets = widgets,
|
||||
playerChrome = playerChrome,
|
||||
actions = actions,
|
||||
)
|
||||
private val testScreen = TestScreen(activity, bindings, screenState, playerChrome, actions)
|
||||
private val mineScreen = MineScreen(activity, bindings, screenState, widgets, actions)
|
||||
@@ -53,6 +49,10 @@ internal class SampleScreenFactory(
|
||||
|
||||
return LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
setBackgroundColor(SampleTheme.background)
|
||||
addView(widgets.moduleTabBar(actions))
|
||||
when (screenState.activeModule) {
|
||||
@@ -65,24 +65,19 @@ internal class SampleScreenFactory(
|
||||
}
|
||||
|
||||
fun renderCatalog() {
|
||||
when (screenState.activeModule) {
|
||||
Module.COURSE -> courseScreen.renderCatalog()
|
||||
Module.TRAIN -> trainScreen.renderCatalogStrip()
|
||||
else -> Unit
|
||||
if (screenState.activeModule == Module.COURSE) {
|
||||
courseScreen.renderCatalog()
|
||||
}
|
||||
}
|
||||
|
||||
fun showCatalogLoading() {
|
||||
when (screenState.activeModule) {
|
||||
Module.COURSE -> courseScreen.showLoading()
|
||||
Module.TRAIN -> trainScreen.showCatalogLoading()
|
||||
else -> Unit
|
||||
if (screenState.activeModule == Module.COURSE) {
|
||||
courseScreen.showLoading()
|
||||
}
|
||||
}
|
||||
|
||||
fun setCatalogStatus(text: String) {
|
||||
courseScreen.setStatus(text)
|
||||
trainScreen.setCatalogStatus(text)
|
||||
}
|
||||
|
||||
fun refreshMyContent(userApi: UserApi, token: String) {
|
||||
|
||||
@@ -4,7 +4,10 @@ import android.app.Activity
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.InsetDrawable
|
||||
import android.graphics.Typeface
|
||||
import android.text.TextUtils
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -14,6 +17,7 @@ import android.widget.FrameLayout
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ScrollView
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Switch
|
||||
import android.widget.TextView
|
||||
@@ -40,26 +44,50 @@ internal class PlayerChrome(
|
||||
override val uiContext get() = activity
|
||||
|
||||
fun createPlayerSection(): View =
|
||||
FrameLayout(activity).apply {
|
||||
LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
background = rounded(SampleTheme.surface, 8f, SampleTheme.border)
|
||||
clipToOutline = true
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
0,
|
||||
1f,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
).withMargins(16.dp, 0, 16.dp, 10.dp)
|
||||
addView(createPlayerView())
|
||||
addView(createPlayerOverlay())
|
||||
addView(
|
||||
createRotationButton().apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
44.dp,
|
||||
44.dp,
|
||||
Gravity.BOTTOM or Gravity.END,
|
||||
).apply {
|
||||
marginEnd = 12.dp
|
||||
bottomMargin = 12.dp
|
||||
createPlayerOverlay().apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
},
|
||||
)
|
||||
addView(
|
||||
object : FrameLayout(activity) {
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val height = (width * 9) / 16
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
|
||||
)
|
||||
}
|
||||
}.apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
addView(createPlayerView())
|
||||
addView(
|
||||
createRotationButton().apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
44.dp,
|
||||
44.dp,
|
||||
Gravity.BOTTOM or Gravity.END,
|
||||
).apply {
|
||||
marginEnd = 12.dp
|
||||
bottomMargin = 12.dp
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -70,7 +98,8 @@ internal class PlayerChrome(
|
||||
setPadding(18.dp, 16.dp, 18.dp, 16.dp)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
0,
|
||||
1f,
|
||||
).withMargins(16.dp, 0, 16.dp, 12.dp)
|
||||
}
|
||||
|
||||
@@ -78,7 +107,15 @@ internal class PlayerChrome(
|
||||
FrameLayout(activity).apply {
|
||||
setBackgroundColor(Color.BLACK)
|
||||
addView(createPlayerView())
|
||||
addView(createPlayerOverlay())
|
||||
addView(
|
||||
createPlayerOverlay().apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
Gravity.TOP,
|
||||
)
|
||||
},
|
||||
)
|
||||
val subtitleContainer = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
@@ -106,6 +143,7 @@ internal class PlayerChrome(
|
||||
textSize = 17f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
maxLines = 1
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
}
|
||||
bindings.sentenceMetaText = TextView(activity).apply {
|
||||
setTextColor(if (translucent) SampleTheme.accentLight else SampleTheme.accentDeep)
|
||||
@@ -157,28 +195,50 @@ internal class PlayerChrome(
|
||||
background = rounded(SampleTheme.overlayFaint, 0f, null)
|
||||
setPadding(18.dp, 14.dp, 18.dp, 16.dp)
|
||||
}
|
||||
addView(bindings.lessonTitleText)
|
||||
if (screenState.activeModule == Module.TRAIN) {
|
||||
addView(createContinuousPlaybackRow(translucent))
|
||||
val topContent = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
addView(bindings.lessonTitleText)
|
||||
if (screenState.activeModule == Module.TRAIN) {
|
||||
addView(createContinuousPlaybackRow(translucent))
|
||||
}
|
||||
addView(bindings.sentenceMetaText)
|
||||
addView(LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.TOP
|
||||
addView(
|
||||
bindings.sentenceText,
|
||||
LinearLayout.LayoutParams(
|
||||
0,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
1f,
|
||||
),
|
||||
)
|
||||
bindings.explainSentenceButton = createExplainButton(translucent)
|
||||
addView(
|
||||
bindings.explainSentenceButton,
|
||||
LinearLayout.LayoutParams(32.dp, 32.dp).apply { marginStart = 10.dp },
|
||||
)
|
||||
})
|
||||
}
|
||||
addView(bindings.sentenceMetaText)
|
||||
addView(LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
addView(
|
||||
bindings.sentenceText,
|
||||
LinearLayout.LayoutParams(
|
||||
0,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
1f,
|
||||
),
|
||||
)
|
||||
bindings.explainSentenceButton = createExplainButton(translucent)
|
||||
addView(
|
||||
bindings.explainSentenceButton,
|
||||
LinearLayout.LayoutParams(32.dp, 32.dp).apply { marginStart = 10.dp },
|
||||
)
|
||||
})
|
||||
addView(
|
||||
ScrollView(activity).apply {
|
||||
isFillViewport = true
|
||||
isVerticalScrollBarEnabled = false
|
||||
overScrollMode = View.OVER_SCROLL_NEVER
|
||||
addView(
|
||||
topContent,
|
||||
LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
),
|
||||
)
|
||||
},
|
||||
LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
if (translucent) ViewGroup.LayoutParams.WRAP_CONTENT else 0,
|
||||
if (translucent) 0f else 1f,
|
||||
),
|
||||
)
|
||||
addView(bindings.progressBar)
|
||||
addView(bindings.statusText)
|
||||
}
|
||||
@@ -197,12 +257,7 @@ internal class PlayerChrome(
|
||||
val overlay = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
setPadding(12.dp, 12.dp, 12.dp, 12.dp)
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
Gravity.TOP,
|
||||
)
|
||||
setPadding(12.dp, 10.dp, 12.dp, 8.dp)
|
||||
}
|
||||
bindings.timeText = widgets.overlayPill("--:-- / --:--").apply {
|
||||
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
|
||||
@@ -217,11 +272,27 @@ internal class PlayerChrome(
|
||||
ImageButton(activity).apply {
|
||||
setImageResource(R.drawable.ic_screen_rotation)
|
||||
imageTintList = ColorStateList.valueOf(Color.WHITE)
|
||||
background = rounded(Color.argb(178, 9, 12, 16), 22.dp.toFloat())
|
||||
val backgroundSize = 22.dp
|
||||
val buttonSize = 44.dp
|
||||
val inset = (buttonSize - backgroundSize) / 2
|
||||
background = InsetDrawable(
|
||||
GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(Color.argb(102, 9, 12, 16))
|
||||
setSize(backgroundSize, backgroundSize)
|
||||
},
|
||||
inset,
|
||||
)
|
||||
scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||
setPadding(10.dp, 10.dp, 10.dp, 10.dp)
|
||||
contentDescription = "横屏"
|
||||
layoutParams = LinearLayout.LayoutParams(44.dp, 44.dp)
|
||||
minimumWidth = 0
|
||||
minimumHeight = 0
|
||||
clipToOutline = false
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
foreground = null
|
||||
}
|
||||
layoutParams = LinearLayout.LayoutParams(buttonSize, buttonSize)
|
||||
setOnClickListener {
|
||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
}
|
||||
|
||||
@@ -153,40 +153,4 @@ internal class SampleWidgets(
|
||||
}
|
||||
}
|
||||
|
||||
fun videoCard(
|
||||
id: String,
|
||||
title: String,
|
||||
meta: String,
|
||||
onClick: () -> Unit,
|
||||
): View {
|
||||
val selected = screenState.activeItemId == id
|
||||
val titleColor = if (selected) SampleTheme.textDark else Color.WHITE
|
||||
val metaColor = if (selected) SampleTheme.accentDeep else SampleTheme.textMuted
|
||||
return LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
isClickable = true
|
||||
background = if (selected) {
|
||||
rounded(SampleTheme.selected, 8f, SampleTheme.accent)
|
||||
} else {
|
||||
rounded(SampleTheme.surface, 8f, SampleTheme.border)
|
||||
}
|
||||
setPadding(14.dp, 12.dp, 14.dp, 12.dp)
|
||||
layoutParams = LinearLayout.LayoutParams(218.dp, 84.dp).withMargins(0, 0, 10.dp, 0)
|
||||
setOnClickListener { onClick() }
|
||||
addView(TextView(activity).apply {
|
||||
text = title
|
||||
setTextColor(titleColor)
|
||||
textSize = 15f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
maxLines = 2
|
||||
})
|
||||
addView(TextView(activity).apply {
|
||||
text = meta
|
||||
setTextColor(metaColor)
|
||||
textSize = 12f
|
||||
setPadding(0, 6.dp, 0, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,14 @@ import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import cn.learningpad.oraltrainer.sample.SampleBindings
|
||||
import cn.learningpad.oraltrainer.sample.SampleTheme
|
||||
import cn.learningpad.oraltrainer.sample.TrainScreenActions
|
||||
import cn.learningpad.oraltrainer.sample.TrainingScreenState
|
||||
import cn.learningpad.oraltrainer.sample.UiStyled
|
||||
|
||||
internal class TrainScreen(
|
||||
private val activity: Activity,
|
||||
private val bindings: SampleBindings,
|
||||
private val screenState: TrainingScreenState,
|
||||
private val widgets: SampleWidgets,
|
||||
private val playerChrome: PlayerChrome,
|
||||
private val actions: TrainScreenActions,
|
||||
) : UiStyled {
|
||||
override val uiContext get() = activity
|
||||
|
||||
@@ -29,47 +20,13 @@ internal class TrainScreen(
|
||||
root.addView(createHeader())
|
||||
root.addView(playerChrome.createPlayerSection())
|
||||
root.addView(playerChrome.createSentenceSection())
|
||||
root.addView(createCatalogSection())
|
||||
}
|
||||
|
||||
fun renderCatalogStrip() {
|
||||
if (!bindings.hasCatalogChrome) return
|
||||
bindings.catalogList.removeAllViews()
|
||||
bindings.catalogStatusText.text = screenState.catalogStatus
|
||||
screenState.catalogVideos.forEach { video ->
|
||||
val enrolled = screenState.courseEnrollment[video.videoHash] == true
|
||||
bindings.catalogList.addView(
|
||||
widgets.videoCard(
|
||||
id = video.videoHash,
|
||||
title = video.title,
|
||||
meta = if (enrolled) {
|
||||
"已选修 · ${video.sentenceCount} 句"
|
||||
} else {
|
||||
"未选修 · 点击选修并进入"
|
||||
},
|
||||
onClick = { actions.onTrainCatalogVideoClick(video) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun showCatalogLoading() {
|
||||
if (!bindings.hasCatalogChrome) return
|
||||
bindings.catalogList.removeAllViews()
|
||||
bindings.catalogList.addView(widgets.infoCard("课程加载中..."))
|
||||
}
|
||||
|
||||
fun setCatalogStatus(text: String) {
|
||||
if (bindings.hasCatalogChrome) {
|
||||
bindings.catalogStatusText.text = text
|
||||
}
|
||||
}
|
||||
|
||||
private fun createHeader(): View {
|
||||
val titleBlock = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
return LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
|
||||
setPadding(18.dp, 16.dp, 18.dp, 12.dp)
|
||||
addView(TextView(activity).apply {
|
||||
text = "八哥口语"
|
||||
setTextColor(Color.WHITE)
|
||||
@@ -80,52 +37,10 @@ internal class TrainScreen(
|
||||
addView(TextView(activity).apply {
|
||||
text = "外语跟读训练神器"
|
||||
setTextColor(SampleTheme.textMuted)
|
||||
textSize = 20f
|
||||
setPadding(1.dp, 4.dp, 0, 0)
|
||||
textSize = 18f
|
||||
includeFontPadding = false
|
||||
setPadding(12.dp, 6.dp, 0, 0)
|
||||
})
|
||||
}
|
||||
return LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
setPadding(18.dp, 16.dp, 18.dp, 12.dp)
|
||||
addView(titleBlock)
|
||||
addView(widgets.headerButton("导入") { actions.onImportLocalVideo() })
|
||||
addView(widgets.headerButton("刷新") { actions.onRefreshCatalog() })
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCatalogSection(): View {
|
||||
bindings.catalogStatusText = TextView(activity).apply {
|
||||
setTextColor(SampleTheme.textMuted)
|
||||
textSize = 13f
|
||||
gravity = Gravity.END
|
||||
text = "正在同步"
|
||||
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
|
||||
}
|
||||
bindings.catalogList = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
setPadding(0, 10.dp, 0, 4.dp)
|
||||
}
|
||||
val header = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
addView(TextView(activity).apply {
|
||||
text = "课程"
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 16f
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
})
|
||||
addView(bindings.catalogStatusText)
|
||||
}
|
||||
val scroller = HorizontalScrollView(activity).apply {
|
||||
isHorizontalScrollBarEnabled = false
|
||||
addView(bindings.catalogList)
|
||||
}
|
||||
return LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(16.dp, 0, 16.dp, 14.dp)
|
||||
addView(header)
|
||||
addView(scroller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user