fixed some bugs
This commit is contained in:
@@ -37,6 +37,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":oral-trainer-sdk"))
|
implementation(project(":oral-trainer-sdk"))
|
||||||
implementation("androidx.core:core-ktx:1.13.1")
|
implementation("androidx.core:core-ktx:1.13.1")
|
||||||
|
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||||
implementation("androidx.media3:media3-common:1.11.0")
|
implementation("androidx.media3:media3-common:1.11.0")
|
||||||
implementation("androidx.media3:media3-exoplayer:1.11.0")
|
implementation("androidx.media3:media3-exoplayer:1.11.0")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,9 @@ class MainActivity : Activity() {
|
|||||||
if (screenState.activeModule == Module.MINE) {
|
if (screenState.activeModule == Module.MINE) {
|
||||||
screenFactory.refreshMyContent(sdk.userApi, authSessionStore.session?.token.orEmpty())
|
screenFactory.refreshMyContent(sdk.userApi, authSessionStore.session?.token.orEmpty())
|
||||||
}
|
}
|
||||||
|
if (screenState.activeModule == Module.READING) {
|
||||||
|
screenFactory.scrollToReadingCurrentSentence()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshCurrentUi() {
|
private fun refreshCurrentUi() {
|
||||||
@@ -552,6 +555,17 @@ class MainActivity : Activity() {
|
|||||||
return sentenceFavoriteStore.toggle(favoriteUserKey(), item.id, sentence.index)
|
return sentenceFavoriteStore.toggle(favoriteUserKey(), item.id, sentence.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSentenceDoubleTapped(sentence: SentenceBoundary) {
|
||||||
|
screenState.continuousPlaybackEnabled = false
|
||||||
|
switchModule(Module.TRAIN)
|
||||||
|
controller.setContinuousPlayback(false)
|
||||||
|
controller.seekTo(sentence.startMs)
|
||||||
|
controller.play()
|
||||||
|
if (bindings.hasPlayerChrome) {
|
||||||
|
bindings.statusText.text = "已定位到第 ${sentence.index + 1} 句,单句循环播放中"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onExplainCurrentSentence() {
|
override fun onExplainCurrentSentence() {
|
||||||
val sentence = controller.currentSentence() ?: return
|
val sentence = controller.currentSentence() ?: return
|
||||||
if (sentence.text.isNullOrBlank()) return
|
if (sentence.text.isNullOrBlank()) return
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ internal interface ReadingScreenActions {
|
|||||||
fun onReadingTranslationVisible(visible: Boolean)
|
fun onReadingTranslationVisible(visible: Boolean)
|
||||||
fun isSentenceFavorite(sentence: SentenceBoundary): Boolean
|
fun isSentenceFavorite(sentence: SentenceBoundary): Boolean
|
||||||
fun toggleSentenceFavorite(sentence: SentenceBoundary): Boolean
|
fun toggleSentenceFavorite(sentence: SentenceBoundary): Boolean
|
||||||
|
fun onSentenceDoubleTapped(sentence: SentenceBoundary)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface TestScreenActions :
|
internal interface TestScreenActions :
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ internal class SampleScreenFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun scrollToReadingCurrentSentence() {
|
||||||
|
if (screenState.activeModule == Module.READING) {
|
||||||
|
readingScreen.scrollToCurrentSentence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun renderCatalog() {
|
fun renderCatalog() {
|
||||||
if (screenState.activeModule == Module.COURSE) {
|
if (screenState.activeModule == Module.COURSE) {
|
||||||
courseScreen.renderCatalog()
|
courseScreen.renderCatalog()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ internal object SampleTheme {
|
|||||||
val textDark = Color.rgb(18, 24, 31)
|
val textDark = Color.rgb(18, 24, 31)
|
||||||
val textMuted = Color.rgb(151, 162, 174)
|
val textMuted = Color.rgb(151, 162, 174)
|
||||||
val textSubtle = Color.rgb(88, 98, 108)
|
val textSubtle = Color.rgb(88, 98, 108)
|
||||||
|
val readingTranslation = Color.rgb(147, 197, 253)
|
||||||
val danger = Color.rgb(220, 38, 38)
|
val danger = Color.rgb(220, 38, 38)
|
||||||
val textOverlay = Color.rgb(163, 172, 182)
|
val textOverlay = Color.rgb(163, 172, 182)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ package cn.learningpad.oraltrainer.sample.ui
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.os.SystemClock
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.ScrollView
|
|
||||||
import android.widget.Switch
|
import android.widget.Switch
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import cn.learningpad.oraltrainer.sample.ReadingScreenActions
|
import cn.learningpad.oraltrainer.sample.ReadingScreenActions
|
||||||
import cn.learningpad.oraltrainer.sample.SampleTheme
|
import cn.learningpad.oraltrainer.sample.SampleTheme
|
||||||
import cn.learningpad.oraltrainer.sample.TrainingScreenState
|
import cn.learningpad.oraltrainer.sample.TrainingScreenState
|
||||||
@@ -18,7 +20,7 @@ import cn.learningpad.oraltrainer.sample.UiStyled
|
|||||||
import cn.learningpad.oraltrainer.sdk.OralTrainerController
|
import cn.learningpad.oraltrainer.sdk.OralTrainerController
|
||||||
import cn.learningpad.oraltrainer.sdk.SentenceBoundary
|
import cn.learningpad.oraltrainer.sdk.SentenceBoundary
|
||||||
|
|
||||||
/** Scrollable full-text reading view for the selected course. */
|
/** Lazy-loading full-text reading view for the selected course. */
|
||||||
internal class ReadingScreen(
|
internal class ReadingScreen(
|
||||||
private val activity: Activity,
|
private val activity: Activity,
|
||||||
private val screenState: TrainingScreenState,
|
private val screenState: TrainingScreenState,
|
||||||
@@ -28,90 +30,81 @@ internal class ReadingScreen(
|
|||||||
) : UiStyled {
|
) : UiStyled {
|
||||||
override val uiContext get() = activity
|
override val uiContext get() = activity
|
||||||
|
|
||||||
private data class SentenceRow(
|
private var readingAdapter: ReadingSentenceAdapter? = null
|
||||||
val translation: TextView,
|
|
||||||
val favoriteButton: Button,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val sentenceRows = mutableMapOf<Int, SentenceRow>()
|
|
||||||
private var sentenceList: LinearLayout? = null
|
|
||||||
private var titleText: TextView? = null
|
private var titleText: TextView? = null
|
||||||
|
private var sentenceListView: RecyclerView? = null
|
||||||
|
private var lastClickedSentenceIndex = Int.MIN_VALUE
|
||||||
|
private var lastClickedAtMs = 0L
|
||||||
|
|
||||||
fun createSection(): View {
|
fun createSection(): View {
|
||||||
val content = LinearLayout(activity).apply {
|
val root = LinearLayout(activity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setPadding(16.dp, 12.dp, 16.dp, 16.dp)
|
setPadding(16.dp, 12.dp, 16.dp, 16.dp)
|
||||||
}
|
}
|
||||||
val list = LinearLayout(activity).apply {
|
val adapter = ReadingSentenceAdapter()
|
||||||
orientation = LinearLayout.VERTICAL
|
readingAdapter = adapter
|
||||||
}
|
val list = RecyclerView(activity).apply {
|
||||||
sentenceList = list
|
layoutManager = LinearLayoutManager(activity)
|
||||||
|
this.adapter = adapter
|
||||||
val title = TextView(activity).apply {
|
itemAnimator = null
|
||||||
setTextColor(Color.WHITE)
|
setItemViewCacheSize(12)
|
||||||
textSize = 24f
|
clipToPadding = false
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
setPadding(0, 0, 0, 16.dp)
|
||||||
setPadding(0, 0, 0, 6.dp)
|
|
||||||
}
|
|
||||||
titleText = title
|
|
||||||
content.addView(title)
|
|
||||||
|
|
||||||
content.addView(createTranslationToggle())
|
|
||||||
content.addView(list)
|
|
||||||
renderSentences()
|
|
||||||
|
|
||||||
return ScrollView(activity).apply {
|
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
0,
|
0,
|
||||||
1f,
|
1f,
|
||||||
)
|
)
|
||||||
addView(content)
|
|
||||||
}
|
}
|
||||||
|
sentenceListView = list
|
||||||
|
|
||||||
|
val title = TextView(activity).apply {
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 15f
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
setPadding(0, 0, 0, 6.dp)
|
||||||
|
}
|
||||||
|
titleText = title
|
||||||
|
root.addView(title)
|
||||||
|
root.addView(createTranslationToggle())
|
||||||
|
root.addView(list)
|
||||||
|
renderSentences()
|
||||||
|
|
||||||
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renderSentences() {
|
fun renderSentences() {
|
||||||
val list = sentenceList ?: return
|
|
||||||
val item = controller.currentTrainingItem()
|
val item = controller.currentTrainingItem()
|
||||||
titleText?.text = screenState.currentCourse?.title.orEmpty()
|
val baseTitle = screenState.currentCourse?.title.orEmpty()
|
||||||
.ifBlank { item?.title ?: "阅读模式" }
|
.ifBlank { item?.title ?: "阅读模式" }
|
||||||
sentenceRows.clear()
|
val sentences = item?.sentences.orEmpty()
|
||||||
list.removeAllViews()
|
titleText?.text = if (sentences.isEmpty()) {
|
||||||
if (item == null || item.sentences.isEmpty()) {
|
baseTitle
|
||||||
list.addView(widgets.infoCard("当前课程还没有可阅读的句子文本"))
|
} else {
|
||||||
return
|
"$baseTitle · ${sentences.size} 句"
|
||||||
}
|
|
||||||
val currentTitle = titleText?.text?.toString().orEmpty()
|
|
||||||
titleText?.text = "$currentTitle · ${item.sentences.size} 句"
|
|
||||||
item.sentences.forEach { sentence ->
|
|
||||||
list.addView(sentenceRow(sentence))
|
|
||||||
}
|
}
|
||||||
|
readingAdapter?.submitSentences(sentences)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshTranslations() {
|
fun refreshTranslations() {
|
||||||
val item = controller.currentTrainingItem() ?: return
|
readingAdapter?.notifyDataSetChanged()
|
||||||
item.sentences.forEach { sentence ->
|
|
||||||
val row = sentenceRows[sentence.index] ?: return@forEach
|
|
||||||
row.translation.visibility = if (screenState.readingTranslationVisible) {
|
|
||||||
View.VISIBLE
|
|
||||||
} else {
|
|
||||||
View.GONE
|
|
||||||
}
|
|
||||||
row.translation.text = translationText(sentence)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshFavorites() {
|
fun refreshFavorites() {
|
||||||
val item = controller.currentTrainingItem() ?: return
|
readingAdapter?.notifyDataSetChanged()
|
||||||
item.sentences.forEach { sentence ->
|
|
||||||
sentenceRows[sentence.index]?.favoriteButton?.apply {
|
|
||||||
val isFavorite = actions.isSentenceFavorite(sentence)
|
|
||||||
setText(if (isFavorite) "已收藏" else "收藏")
|
|
||||||
background = rounded(
|
|
||||||
if (isFavorite) SampleTheme.accent else SampleTheme.button,
|
|
||||||
8f,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun scrollToCurrentSentence() {
|
||||||
|
val list = sentenceListView ?: return
|
||||||
|
val currentSentence = controller.currentSentence() ?: return
|
||||||
|
val item = controller.currentTrainingItem() ?: return
|
||||||
|
val position = item.sentences.indexOfFirst { it.index == currentSentence.index }
|
||||||
|
if (position < 0) return
|
||||||
|
list.post {
|
||||||
|
if (sentenceListView !== list) return@post
|
||||||
|
val layoutManager = list.layoutManager as? LinearLayoutManager ?: return@post
|
||||||
|
val offset = (list.height * 0.25f).toInt().coerceAtLeast(0)
|
||||||
|
layoutManager.scrollToPositionWithOffset(position, offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +129,84 @@ internal class ReadingScreen(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sentenceRow(sentence: SentenceBoundary): View {
|
private fun translationText(sentence: SentenceBoundary): String = when {
|
||||||
|
!sentence.zhText.isNullOrBlank() -> sentence.zhText.orEmpty()
|
||||||
|
screenState.chineseTranslationLoading -> "正在翻译中文…"
|
||||||
|
screenState.chineseTranslationError.isNotBlank() -> screenState.chineseTranslationError
|
||||||
|
else -> "暂无中文翻译"
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ReadingSentenceAdapter :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private var sentences: List<SentenceBoundary> = emptyList()
|
||||||
|
|
||||||
|
init {
|
||||||
|
setHasStableIds(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemId(position: Int): Long {
|
||||||
|
return if (sentences.isEmpty()) EMPTY_ID else sentences[position].index.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (sentences.isEmpty()) TYPE_EMPTY else TYPE_SENTENCE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return if (viewType == TYPE_EMPTY) {
|
||||||
|
val empty = TextView(activity).apply {
|
||||||
|
text = "当前课程还没有可阅读的句子文本"
|
||||||
|
setTextColor(SampleTheme.textDark)
|
||||||
|
textSize = 13f
|
||||||
|
background = rounded(SampleTheme.selected, 8f)
|
||||||
|
setPadding(12.dp, 10.dp, 12.dp, 10.dp)
|
||||||
|
layoutParams = RecyclerView.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
EmptyViewHolder(empty)
|
||||||
|
} else {
|
||||||
|
SentenceViewHolder(createSentenceView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
if (holder !is SentenceViewHolder || sentences.isEmpty()) return
|
||||||
|
val sentence = sentences[position]
|
||||||
|
attachDoubleTap(holder.itemView, sentence)
|
||||||
|
holder.text.text =
|
||||||
|
"${sentence.index + 1}. ${sentence.text?.takeIf { it.isNotBlank() } ?: "未提供文本"}"
|
||||||
|
holder.translation.visibility = if (screenState.readingTranslationVisible) {
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
|
}
|
||||||
|
holder.translation.text = translationText(sentence)
|
||||||
|
bindFavoriteButton(holder.favoriteButton, sentence)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return if (sentences.isEmpty()) 1 else sentences.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitSentences(updated: List<SentenceBoundary>) {
|
||||||
|
sentences = updated
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSentenceView(): View {
|
||||||
|
val text = TextView(activity).apply {
|
||||||
|
setTextColor(Color.WHITE)
|
||||||
|
textSize = 16f
|
||||||
|
}
|
||||||
val translation = TextView(activity).apply {
|
val translation = TextView(activity).apply {
|
||||||
text = translationText(sentence)
|
setTextColor(SampleTheme.readingTranslation)
|
||||||
setTextColor(SampleTheme.textSubtle)
|
|
||||||
textSize = 14f
|
textSize = 14f
|
||||||
setPadding(0, 6.dp, 0, 0)
|
setPadding(0, 6.dp, 0, 0)
|
||||||
visibility = if (screenState.readingTranslationVisible) View.VISIBLE else View.GONE
|
|
||||||
}
|
}
|
||||||
val favoriteButton = Button(activity).apply {
|
val favoriteButton = Button(activity).apply {
|
||||||
setText("收藏")
|
|
||||||
isAllCaps = false
|
isAllCaps = false
|
||||||
setTextColor(Color.WHITE)
|
setTextColor(Color.WHITE)
|
||||||
textSize = 12f
|
textSize = 12f
|
||||||
@@ -155,56 +216,91 @@ internal class ReadingScreen(
|
|||||||
minimumWidth = 0
|
minimumWidth = 0
|
||||||
minimumHeight = 0
|
minimumHeight = 0
|
||||||
setPadding(12.dp, 0, 12.dp, 0)
|
setPadding(12.dp, 0, 12.dp, 0)
|
||||||
background = rounded(SampleTheme.button, 8f)
|
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
32.dp,
|
32.dp,
|
||||||
).withMargins(10.dp, 0, 0, 0)
|
).withMargins(10.dp, 0, 0, 0)
|
||||||
setOnClickListener {
|
|
||||||
val isFavorite = actions.toggleSentenceFavorite(sentence)
|
|
||||||
setText(if (isFavorite) "已收藏" else "收藏")
|
|
||||||
background = rounded(
|
|
||||||
if (isFavorite) SampleTheme.accent else SampleTheme.button,
|
|
||||||
8f,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val isFavorite = actions.isSentenceFavorite(sentence)
|
|
||||||
favoriteButton.setText(if (isFavorite) "已收藏" else "收藏")
|
|
||||||
favoriteButton.background = rounded(
|
|
||||||
if (isFavorite) SampleTheme.accent else SampleTheme.button,
|
|
||||||
8f,
|
|
||||||
)
|
|
||||||
val textColumn = LinearLayout(activity).apply {
|
val textColumn = LinearLayout(activity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
|
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
addView(TextView(activity).apply {
|
addView(text)
|
||||||
text = "${sentence.index + 1}. ${sentence.text?.takeIf { it.isNotBlank() } ?: "未提供文本"}"
|
|
||||||
setTextColor(Color.WHITE)
|
|
||||||
textSize = 16f
|
|
||||||
})
|
|
||||||
addView(translation)
|
addView(translation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return LinearLayout(activity).apply {
|
return LinearLayout(activity).apply {
|
||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
gravity = Gravity.CENTER_VERTICAL
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
background = rounded(SampleTheme.surface, 10f, SampleTheme.border)
|
background = rounded(SampleTheme.surface, 10f, SampleTheme.border)
|
||||||
setPadding(14.dp, 12.dp, 10.dp, 12.dp)
|
setPadding(14.dp, 12.dp, 10.dp, 12.dp)
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = RecyclerView.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
).withMargins(0, 0, 0, 10.dp)
|
).apply {
|
||||||
|
bottomMargin = 10.dp
|
||||||
|
}
|
||||||
addView(textColumn)
|
addView(textColumn)
|
||||||
addView(favoriteButton)
|
addView(favoriteButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translationText(sentence: SentenceBoundary): String = when {
|
private fun bindFavoriteButton(button: Button, sentence: SentenceBoundary) {
|
||||||
!sentence.zhText.isNullOrBlank() -> sentence.zhText.orEmpty()
|
val isFavorite = actions.isSentenceFavorite(sentence)
|
||||||
screenState.chineseTranslationLoading -> "正在翻译中文…"
|
button.setText(if (isFavorite) "已收藏" else "收藏")
|
||||||
screenState.chineseTranslationError.isNotBlank() -> screenState.chineseTranslationError
|
button.background = rounded(
|
||||||
else -> "暂无中文翻译"
|
if (isFavorite) SampleTheme.accent else SampleTheme.button,
|
||||||
|
8f,
|
||||||
|
)
|
||||||
|
button.setOnClickListener {
|
||||||
|
val favorite = actions.toggleSentenceFavorite(sentence)
|
||||||
|
button.setText(if (favorite) "已收藏" else "收藏")
|
||||||
|
button.background = rounded(
|
||||||
|
if (favorite) SampleTheme.accent else SampleTheme.button,
|
||||||
|
8f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachDoubleTap(view: View, sentence: SentenceBoundary) {
|
||||||
|
view.isClickable = true
|
||||||
|
view.setOnClickListener {
|
||||||
|
val now = SystemClock.elapsedRealtime()
|
||||||
|
val isDoubleTap = lastClickedSentenceIndex == sentence.index &&
|
||||||
|
now - lastClickedAtMs <= DOUBLE_TAP_INTERVAL_MS
|
||||||
|
if (isDoubleTap) {
|
||||||
|
lastClickedSentenceIndex = Int.MIN_VALUE
|
||||||
|
lastClickedAtMs = 0L
|
||||||
|
actions.onSentenceDoubleTapped(sentence)
|
||||||
|
} else {
|
||||||
|
lastClickedSentenceIndex = sentence.index
|
||||||
|
lastClickedAtMs = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class EmptyViewHolder(root: View) : RecyclerView.ViewHolder(root)
|
||||||
|
|
||||||
|
private inner class SentenceViewHolder(
|
||||||
|
root: View,
|
||||||
|
) : RecyclerView.ViewHolder(root) {
|
||||||
|
val text: TextView
|
||||||
|
val translation: TextView
|
||||||
|
val favoriteButton: Button
|
||||||
|
|
||||||
|
init {
|
||||||
|
val textColumn = root as ViewGroup
|
||||||
|
val content = textColumn.getChildAt(0) as ViewGroup
|
||||||
|
text = content.getChildAt(0) as TextView
|
||||||
|
translation = content.getChildAt(1) as TextView
|
||||||
|
favoriteButton = textColumn.getChildAt(1) as Button
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val TYPE_EMPTY = 0
|
||||||
|
private const val TYPE_SENTENCE = 1
|
||||||
|
private const val EMPTY_ID = Long.MIN_VALUE
|
||||||
|
private const val DOUBLE_TAP_INTERVAL_MS = 300L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user