react-native-advanced-text 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/advancedtext/AdvancedTextView.kt +303 -296
- package/android/src/main/java/com/advancedtext/AdvancedTextViewManager.kt +95 -101
- package/lib/module/AdvancedText.js +2 -6
- package/lib/module/AdvancedText.js.map +1 -1
- package/lib/module/AdvancedTextViewNativeComponent.ts +4 -6
- package/lib/typescript/src/AdvancedText.d.ts +0 -2
- package/lib/typescript/src/AdvancedText.d.ts.map +1 -1
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts +2 -5
- package/lib/typescript/src/AdvancedTextViewNativeComponent.d.ts.map +1 -1
- package/package.json +175 -175
- package/src/AdvancedText.tsx +1 -6
- package/src/AdvancedTextViewNativeComponent.ts +4 -6
|
@@ -1,296 +1,303 @@
|
|
|
1
|
-
package com.advancedtext
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.graphics.Color
|
|
5
|
-
import android.text.SpannableString
|
|
6
|
-
import android.text.Spanned
|
|
7
|
-
import android.text.TextPaint
|
|
8
|
-
import android.text.method.LinkMovementMethod
|
|
9
|
-
import android.text.style.ClickableSpan
|
|
10
|
-
import android.text.style.BackgroundColorSpan
|
|
11
|
-
import android.
|
|
12
|
-
import android.util.
|
|
13
|
-
import android.util.
|
|
14
|
-
import android.view.ActionMode
|
|
15
|
-
import android.view.Menu
|
|
16
|
-
import android.view.MenuItem
|
|
17
|
-
import android.view.View
|
|
18
|
-
import android.widget.TextView
|
|
19
|
-
import com.facebook.react.bridge.Arguments
|
|
20
|
-
import com.facebook.react.bridge.ReactContext
|
|
21
|
-
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
22
|
-
import android.text.Selection
|
|
23
|
-
|
|
24
|
-
class AdvancedTextView : TextView {
|
|
25
|
-
|
|
26
|
-
private val TAG = "AdvancedTextView"
|
|
27
|
-
|
|
28
|
-
private var highlightedWords: List<HighlightedWord> = emptyList()
|
|
29
|
-
private var menuOptions: List<String> = emptyList()
|
|
30
|
-
private var indicatorWordIndex: Int = -1
|
|
31
|
-
private var lastSelectedText: String = ""
|
|
32
|
-
private var isSelectionEnabled: Boolean = true
|
|
33
|
-
private var customActionMode: ActionMode? = null
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
fun
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
spannableString.setSpan(
|
|
185
|
-
|
|
186
|
-
wordStart,
|
|
187
|
-
wordEnd,
|
|
188
|
-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
Log.d(TAG, "
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
)
|
|
1
|
+
package com.advancedtext
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.text.SpannableString
|
|
6
|
+
import android.text.Spanned
|
|
7
|
+
import android.text.TextPaint
|
|
8
|
+
import android.text.method.LinkMovementMethod
|
|
9
|
+
import android.text.style.ClickableSpan
|
|
10
|
+
import android.text.style.BackgroundColorSpan
|
|
11
|
+
import android.text.style.ForegroundColorSpan
|
|
12
|
+
import android.util.AttributeSet
|
|
13
|
+
import android.util.Log
|
|
14
|
+
import android.view.ActionMode
|
|
15
|
+
import android.view.Menu
|
|
16
|
+
import android.view.MenuItem
|
|
17
|
+
import android.view.View
|
|
18
|
+
import android.widget.TextView
|
|
19
|
+
import com.facebook.react.bridge.Arguments
|
|
20
|
+
import com.facebook.react.bridge.ReactContext
|
|
21
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
22
|
+
import android.text.Selection
|
|
23
|
+
|
|
24
|
+
class AdvancedTextView : TextView {
|
|
25
|
+
|
|
26
|
+
private val TAG = "AdvancedTextView"
|
|
27
|
+
|
|
28
|
+
private var highlightedWords: List<HighlightedWord> = emptyList()
|
|
29
|
+
private var menuOptions: List<String> = emptyList()
|
|
30
|
+
private var indicatorWordIndex: Int = -1
|
|
31
|
+
private var lastSelectedText: String = ""
|
|
32
|
+
private var isSelectionEnabled: Boolean = true
|
|
33
|
+
private var customActionMode: ActionMode? = null
|
|
34
|
+
|
|
35
|
+
constructor(context: Context?) : super(context) { init() }
|
|
36
|
+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
37
|
+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
38
|
+
|
|
39
|
+
private fun init() {
|
|
40
|
+
Log.d(TAG, "AdvancedTextView initialized")
|
|
41
|
+
|
|
42
|
+
// Set default text appearance - DON'T set black color here
|
|
43
|
+
textSize = 16f
|
|
44
|
+
setPadding(16, 16, 16, 16)
|
|
45
|
+
|
|
46
|
+
movementMethod = LinkMovementMethod.getInstance()
|
|
47
|
+
setTextIsSelectable(true)
|
|
48
|
+
|
|
49
|
+
customSelectionActionModeCallback = object : ActionMode.Callback {
|
|
50
|
+
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
51
|
+
Log.d(TAG, "onCreateActionMode triggered")
|
|
52
|
+
customActionMode = mode
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
57
|
+
Log.d(TAG, "onPrepareActionMode triggered")
|
|
58
|
+
menu?.clear()
|
|
59
|
+
|
|
60
|
+
val selectionStart = selectionStart
|
|
61
|
+
val selectionEnd = selectionEnd
|
|
62
|
+
|
|
63
|
+
if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
|
|
64
|
+
lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
|
|
65
|
+
Log.d(TAG, "User selected text: '$lastSelectedText'")
|
|
66
|
+
Log.d(TAG, "Menu options available: $menuOptions")
|
|
67
|
+
|
|
68
|
+
menuOptions.forEachIndexed { index, option ->
|
|
69
|
+
menu?.add(0, index, index, option)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
sendSelectionEvent(lastSelectedText, "selection")
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
|
79
|
+
item?.let {
|
|
80
|
+
val menuItemText = it.title.toString()
|
|
81
|
+
Log.d(TAG, "Menu item clicked: $menuItemText")
|
|
82
|
+
sendSelectionEvent(lastSelectedText, menuItemText)
|
|
83
|
+
mode?.finish()
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun onDestroyActionMode(mode: ActionMode?) {
|
|
90
|
+
Log.d(TAG, "onDestroyActionMode")
|
|
91
|
+
customActionMode = null
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fun setAdvancedText(text: String) {
|
|
97
|
+
Log.d(TAG, "setAdvancedText: $text (length=${text.length})")
|
|
98
|
+
|
|
99
|
+
// Set the text first
|
|
100
|
+
super.setText(text, BufferType.SPANNABLE)
|
|
101
|
+
|
|
102
|
+
// Then apply highlights
|
|
103
|
+
updateTextWithHighlights()
|
|
104
|
+
|
|
105
|
+
// Force layout update
|
|
106
|
+
requestLayout()
|
|
107
|
+
invalidate()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fun setMenuOptions(menuOptions: List<String>) {
|
|
111
|
+
Log.d(TAG, "setMenuOptions received from RN: $menuOptions")
|
|
112
|
+
this.menuOptions = menuOptions
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fun setHighlightedWords(highlightedWords: List<HighlightedWord>) {
|
|
116
|
+
Log.d(TAG, "setHighlightedWords received from RN: $highlightedWords")
|
|
117
|
+
this.highlightedWords = highlightedWords
|
|
118
|
+
updateTextWithHighlights()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fun setIndicatorWordIndex(index: Int) {
|
|
122
|
+
Log.d(TAG, "setIndicatorWordIndex received: $index")
|
|
123
|
+
this.indicatorWordIndex = index
|
|
124
|
+
updateTextWithHighlights()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun updateTextWithHighlights() {
|
|
128
|
+
val textValue = this.text?.toString() ?: ""
|
|
129
|
+
Log.d(TAG, "updateTextWithHighlights called")
|
|
130
|
+
Log.d(TAG, "Current text: $textValue")
|
|
131
|
+
Log.d(TAG, "Highlighted words: $highlightedWords")
|
|
132
|
+
Log.d(TAG, "Indicator index: $indicatorWordIndex")
|
|
133
|
+
|
|
134
|
+
if (textValue.isEmpty()) {
|
|
135
|
+
Log.d(TAG, "No text available, skipping")
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
val spannableString = SpannableString(textValue)
|
|
140
|
+
|
|
141
|
+
// Split words while preserving spaces for accurate indexing
|
|
142
|
+
val words = textValue.split("\\s+".toRegex()).filter { it.isNotEmpty() }
|
|
143
|
+
|
|
144
|
+
var currentIndex = 0
|
|
145
|
+
words.forEachIndexed { wordIndex, word ->
|
|
146
|
+
|
|
147
|
+
// Find the actual position of the word in the text
|
|
148
|
+
val wordStart = textValue.indexOf(word, currentIndex)
|
|
149
|
+
if (wordStart >= 0) {
|
|
150
|
+
val wordEnd = wordStart + word.length
|
|
151
|
+
|
|
152
|
+
Log.d(TAG, "Processing word '$word' at position $wordStart-$wordEnd, index $wordIndex")
|
|
153
|
+
|
|
154
|
+
// Apply clickable span FIRST (this is important)
|
|
155
|
+
spannableString.setSpan(
|
|
156
|
+
WordClickableSpan(wordIndex, word),
|
|
157
|
+
wordStart,
|
|
158
|
+
wordEnd,
|
|
159
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Then apply background color for highlighted words
|
|
163
|
+
highlightedWords.find { it.index == wordIndex }?.let { highlightedWord ->
|
|
164
|
+
val color = try {
|
|
165
|
+
Color.parseColor(highlightedWord.highlightColor)
|
|
166
|
+
} catch (e: IllegalArgumentException) {
|
|
167
|
+
Log.e(TAG, "Invalid color: ${highlightedWord.highlightColor}, using yellow")
|
|
168
|
+
Color.YELLOW
|
|
169
|
+
}
|
|
170
|
+
Log.d(TAG, "Applying highlight to word '$word' at index $wordIndex with color ${highlightedWord.highlightColor}")
|
|
171
|
+
|
|
172
|
+
spannableString.setSpan(
|
|
173
|
+
BackgroundColorSpan(color),
|
|
174
|
+
wordStart,
|
|
175
|
+
wordEnd,
|
|
176
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Then apply indicator span
|
|
181
|
+
if (wordIndex == indicatorWordIndex) {
|
|
182
|
+
Log.d(TAG, "Applying indicator span to word '$word' at index $wordIndex")
|
|
183
|
+
|
|
184
|
+
spannableString.setSpan(
|
|
185
|
+
IndicatorSpan(),
|
|
186
|
+
wordStart,
|
|
187
|
+
wordEnd,
|
|
188
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
currentIndex = wordEnd
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Set the spannable text
|
|
197
|
+
setText(spannableString, BufferType.SPANNABLE)
|
|
198
|
+
|
|
199
|
+
// Ensure movement method is still set
|
|
200
|
+
movementMethod = LinkMovementMethod.getInstance()
|
|
201
|
+
|
|
202
|
+
Log.d(TAG, "Text updated with spans, total spans")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
private fun onMenuItemClick(item: MenuItem, selectedText: String): Boolean {
|
|
208
|
+
val menuItemText = menuOptions[item.itemId]
|
|
209
|
+
Log.d(TAG, "onMenuItemClick: menuOption='$menuItemText', selectedText='$selectedText'")
|
|
210
|
+
sendSelectionEvent(selectedText, menuItemText)
|
|
211
|
+
return true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private fun sendSelectionEvent(selectedText: String, eventType: String) {
|
|
215
|
+
Log.d(TAG, "sendSelectionEvent -> eventType='$eventType' selectedText='$selectedText'")
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
val reactContext = context as? ReactContext ?: return
|
|
219
|
+
val event = Arguments.createMap().apply {
|
|
220
|
+
putString("selectedText", selectedText)
|
|
221
|
+
putString("event", eventType)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
225
|
+
.receiveEvent(id, "onSelection", event)
|
|
226
|
+
} catch (e: Exception) {
|
|
227
|
+
Log.e(TAG, "Error sending selection event", e)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private inner class WordClickableSpan(
|
|
232
|
+
private val wordIndex: Int,
|
|
233
|
+
private val word: String
|
|
234
|
+
) : ClickableSpan() {
|
|
235
|
+
|
|
236
|
+
override fun onClick(widget: View) {
|
|
237
|
+
Log.d(TAG, "WordClickableSpan onClick triggered: '$word' (index=$wordIndex)")
|
|
238
|
+
|
|
239
|
+
// Small delay to ensure the click is processed
|
|
240
|
+
widget.post {
|
|
241
|
+
sendWordPressEvent(word, wordIndex)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
override fun updateDrawState(ds: TextPaint) {
|
|
246
|
+
// Don't call super to avoid default link styling (blue color, underline)
|
|
247
|
+
// Keep the original text appearance
|
|
248
|
+
|
|
249
|
+
ds.isUnderlineText = false
|
|
250
|
+
ds.bgColor = Color.TRANSPARENT
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private inner class IndicatorSpan : ClickableSpan() {
|
|
255
|
+
override fun onClick(widget: View) {
|
|
256
|
+
Log.d(TAG, "IndicatorSpan clicked (shouldn't trigger action)")
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
override fun updateDrawState(ds: TextPaint) {
|
|
260
|
+
|
|
261
|
+
ds.isFakeBoldText = true
|
|
262
|
+
ds.isUnderlineText = false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private fun sendWordPressEvent(word: String, index: Int) {
|
|
267
|
+
Log.d(TAG, "sendWordPressEvent -> word='$word', index=$index")
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
val reactContext = context as? ReactContext ?: return
|
|
271
|
+
val event = Arguments.createMap().apply {
|
|
272
|
+
putString("word", word)
|
|
273
|
+
putInt("index", index)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
277
|
+
.receiveEvent(id, "onWordPress", event)
|
|
278
|
+
} catch (e: Exception) {
|
|
279
|
+
Log.e(TAG, "Error sending word press event", e)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fun clearSelection() {
|
|
284
|
+
Log.d(TAG, "clearSelection called")
|
|
285
|
+
val spannable = this.text as? android.text.Spannable ?: return
|
|
286
|
+
Selection.removeSelection(spannable)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
290
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
291
|
+
Log.d(TAG, "onMeasure: width=${measuredWidth}, height=${measuredHeight}")
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
295
|
+
super.onLayout(changed, left, top, right, bottom)
|
|
296
|
+
Log.d(TAG, "onLayout: changed=$changed, bounds=[$left,$top,$right,$bottom]")
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
data class HighlightedWord(
|
|
301
|
+
val index: Int,
|
|
302
|
+
val highlightColor: String
|
|
303
|
+
)
|