Skip to content

Commit a745f4a

Browse files
authored
Merge pull request #5382 from nextcloud/bugfix/5173/fixMarkdownTasks
fix markdown tasks
2 parents 9ec6f81 + 7280062 commit a745f4a

File tree

4 files changed

+145
-100
lines changed

4 files changed

+145
-100
lines changed

app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import android.util.TypedValue
1515
import android.view.View
1616
import android.widget.CheckBox
1717
import androidx.core.content.ContextCompat
18-
import androidx.core.text.toSpanned
18+
import androidx.emoji2.widget.EmojiTextView
1919
import autodagger.AutoInjector
2020
import coil.load
2121
import com.google.android.flexbox.FlexboxLayout
@@ -150,8 +150,8 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
150150
// binding.messageText.text =
151151
// SpannableStringBuilder(processedMessageText).append(" (" + message.jsonMessageId + ")")
152152
} else {
153-
binding.messageText.visibility = View.GONE
154153
binding.checkboxContainer.visibility = View.VISIBLE
154+
binding.messageText.visibility = View.GONE
155155
}
156156

157157
if (message.lastEditTimestamp != 0L && !message.isDeleted) {
@@ -202,10 +202,10 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
202202
)
203203
}
204204

205+
@Suppress("LongMethod")
205206
private fun processCheckboxes(chatMessage: ChatMessage, user: User): Boolean {
206207
val chatActivity = commonMessageInterface as ChatActivity
207-
val message = chatMessage.message!!.toSpanned()
208-
val messageTextView = binding.messageText
208+
val message = chatMessage.message ?: return false
209209
val checkBoxContainer = binding.checkboxContainer
210210
val isOlderThanTwentyFourHours = chatMessage
211211
.createdAt
@@ -218,46 +218,68 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
218218
!isOlderThanTwentyFourHours
219219

220220
checkBoxContainer.removeAllViews()
221-
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
222-
val matches = regex.findAll(message)
223-
224-
if (matches.none()) return false
225-
226-
val firstPart = message.toString().substringBefore("\n- [")
227-
messageTextView.text = messageUtils.enrichChatMessageText(
228-
binding.messageText.context,
229-
firstPart,
230-
true,
231-
viewThemeUtils
232-
)
221+
val checkboxRegex = """- \[(X|x| )]\s*(.+)""".toRegex()
222+
val lines = message.lines()
233223

234224
val checkboxList = mutableListOf<CheckBox>()
235-
236-
matches.forEach { matchResult ->
237-
val isChecked = matchResult.groupValues[CHECKED_GROUP_INDEX] == "X" ||
238-
matchResult.groupValues[CHECKED_GROUP_INDEX] == "x"
239-
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
240-
241-
val checkBox = CheckBox(checkBoxContainer.context).apply {
242-
text = taskText
243-
this.isChecked = isChecked
244-
this.isEnabled = (
245-
chatMessage.actorType == "bots" ||
246-
chatActivity.userAllowedByPrivilages(chatMessage)
247-
) &&
248-
messageIsEditable
249-
250-
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
251-
252-
setOnCheckedChangeListener { _, _ ->
253-
updateCheckboxStates(chatMessage, user, checkboxList)
225+
var hasCheckbox = false
226+
227+
lines.forEach { line ->
228+
val match = checkboxRegex.matchEntire(line.trim())
229+
if (match != null) {
230+
hasCheckbox = true
231+
val isChecked = match.groupValues[1].equals("X", true)
232+
val taskText = match.groupValues[2].trim()
233+
val checkBox = CheckBox(checkBoxContainer.context).apply {
234+
val messageText = messageUtils.enrichChatMessageText(
235+
context,
236+
taskText,
237+
true,
238+
viewThemeUtils
239+
)
240+
text = messageUtils.processMessageParameters(
241+
context,
242+
viewThemeUtils,
243+
messageText,
244+
chatMessage,
245+
null
246+
)
247+
this.isChecked = isChecked
248+
this.isEnabled = (
249+
chatMessage.actorType == "bots" ||
250+
chatActivity.userAllowedByPrivilages(chatMessage)
251+
) &&
252+
messageIsEditable
253+
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
254+
setOnCheckedChangeListener { _, _ ->
255+
updateCheckboxStates(chatMessage, user, checkboxList)
256+
}
254257
}
258+
checkBoxContainer.addView(checkBox)
259+
checkboxList.add(checkBox)
260+
viewThemeUtils.platform.themeCheckbox(checkBox)
261+
} else if (line.isNotBlank()) {
262+
val textView = EmojiTextView(checkBoxContainer.context).apply {
263+
val messageText = messageUtils.enrichChatMessageText(
264+
context,
265+
line,
266+
true,
267+
viewThemeUtils
268+
)
269+
text = messageUtils.processMessageParameters(
270+
context,
271+
viewThemeUtils,
272+
messageText,
273+
chatMessage,
274+
null
275+
)
276+
viewThemeUtils.platform.colorTextView(this, ColorRole.ON_SURFACE_VARIANT)
277+
}
278+
checkBoxContainer.addView(textView)
255279
}
256-
checkBoxContainer.addView(checkBox)
257-
checkboxList.add(checkBox)
258-
viewThemeUtils.platform.themeCheckbox(checkBox)
259280
}
260-
return true
281+
282+
return hasCheckbox
261283
}
262284

263285
private fun updateCheckboxStates(chatMessage: ChatMessage, user: User, checkboxes: List<CheckBox>) {
@@ -292,17 +314,18 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
292314
}
293315

294316
private fun updateMessageWithCheckboxStates(originalMessage: String, checkboxes: List<CheckBox>): String {
295-
var updatedMessage = originalMessage
296-
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
297-
298-
checkboxes.forEach { _ ->
299-
updatedMessage = regex.replace(updatedMessage) { matchResult ->
300-
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
301-
val checkboxState = if (checkboxes.find { it.text == taskText }?.isChecked == true) "X" else " "
302-
"- [$checkboxState] $taskText"
317+
val checkboxRegex = """- \[(X|x| )]\s*(.+)""".toRegex()
318+
var index = 0
319+
return originalMessage.lines().joinToString("\n") { line ->
320+
val match = checkboxRegex.matchEntire(line.trim())
321+
if (match != null) {
322+
val taskText = match.groupValues[2].trim()
323+
val state = if (checkboxes.getOrNull(index++)?.isChecked == true) "X" else " "
324+
"- [$state] $taskText"
325+
} else {
326+
line
303327
}
304328
}
305-
return updatedMessage
306329
}
307330

308331
private fun longClickOnReaction(chatMessage: ChatMessage) {
@@ -421,8 +444,6 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
421444
companion object {
422445
const val TEXT_SIZE_MULTIPLIER = 2.5
423446
private val TAG = IncomingTextMessageViewHolder::class.java.simpleName
424-
private const val CHECKED_GROUP_INDEX = 2
425-
private const val TASK_TEXT_GROUP_INDEX = 3
426447
private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000
427448
}
428449
}

app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import android.view.View
1616
import android.widget.CheckBox
1717
import androidx.core.content.ContextCompat
1818
import androidx.core.content.res.ResourcesCompat
19-
import androidx.core.text.toSpanned
19+
import androidx.emoji2.widget.EmojiTextView
2020
import androidx.lifecycle.lifecycleScope
2121
import autodagger.AutoInjector
2222
import coil.load
@@ -240,10 +240,10 @@ class OutcomingTextMessageViewHolder(itemView: View) :
240240
)
241241
}
242242

243+
@Suppress("LongMethod")
243244
private fun processCheckboxes(chatMessage: ChatMessage, user: User): Boolean {
244245
val chatActivity = commonMessageInterface as ChatActivity
245-
val message = chatMessage.message!!.toSpanned()
246-
val messageTextView = binding.messageText
246+
val message = chatMessage.message ?: return false
247247
val checkBoxContainer = binding.checkboxContainer
248248
val isOlderThanTwentyFourHours = chatMessage
249249
.createdAt
@@ -262,42 +262,64 @@ class OutcomingTextMessageViewHolder(itemView: View) :
262262
chatActivity.currentConversation?.type == ConversationEnums.ConversationType.NOTE_TO_SELF
263263

264264
checkBoxContainer.removeAllViews()
265-
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
266-
val matches = regex.findAll(message)
267-
268-
if (matches.none()) return false
269-
270-
val firstPart = message.toString().substringBefore("\n- [")
271-
messageTextView.text = messageUtils.enrichChatMessageText(
272-
binding.messageText.context,
273-
firstPart,
274-
true,
275-
viewThemeUtils
276-
)
265+
val checkboxRegex = """- \[(X|x| )]\s*(.+)""".toRegex()
266+
val lines = message.lines()
277267

278268
val checkboxList = mutableListOf<CheckBox>()
279-
280-
matches.forEach { matchResult ->
281-
val isChecked = matchResult.groupValues[CHECKED_GROUP_INDEX] == "X" ||
282-
matchResult.groupValues[CHECKED_GROUP_INDEX] == "x"
283-
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
284-
285-
val checkBox = CheckBox(checkBoxContainer.context).apply {
286-
text = taskText
287-
this.isChecked = isChecked
288-
this.isEnabled = messageIsEditable || isNoTimeLimitOnNoteToSelf
289-
290-
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
291-
292-
setOnCheckedChangeListener { _, _ ->
293-
updateCheckboxStates(chatMessage, user, checkboxList)
269+
var hasCheckbox = false
270+
271+
lines.forEach { line ->
272+
val match = checkboxRegex.matchEntire(line.trim())
273+
if (match != null) {
274+
hasCheckbox = true
275+
val isChecked = match.groupValues[1].equals("X", true)
276+
val taskText = match.groupValues[2].trim()
277+
val checkBox = CheckBox(checkBoxContainer.context).apply {
278+
val messageText = messageUtils.enrichChatMessageText(
279+
context,
280+
taskText,
281+
false,
282+
viewThemeUtils
283+
)
284+
text = messageUtils.processMessageParameters(
285+
context,
286+
viewThemeUtils,
287+
messageText,
288+
chatMessage,
289+
null
290+
)
291+
this.isChecked = isChecked
292+
this.isEnabled = messageIsEditable || isNoTimeLimitOnNoteToSelf
293+
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
294+
setOnCheckedChangeListener { _, _ ->
295+
updateCheckboxStates(chatMessage, user, checkboxList)
296+
}
297+
}
298+
checkBoxContainer.addView(checkBox)
299+
checkboxList.add(checkBox)
300+
viewThemeUtils.platform.themeCheckbox(checkBox)
301+
} else if (line.isNotBlank()) {
302+
val textView = EmojiTextView(checkBoxContainer.context).apply {
303+
val messageText = messageUtils.enrichChatMessageText(
304+
context,
305+
line,
306+
false,
307+
viewThemeUtils
308+
)
309+
text = messageUtils.processMessageParameters(
310+
context,
311+
viewThemeUtils,
312+
messageText,
313+
chatMessage,
314+
null
315+
)
316+
viewThemeUtils.platform.colorTextView(this, ColorRole.ON_SURFACE_VARIANT)
294317
}
318+
checkBoxContainer.addView(textView)
295319
}
296-
checkBoxContainer.addView(checkBox)
297-
checkboxList.add(checkBox)
298-
viewThemeUtils.platform.themeCheckbox(checkBox)
299320
}
300-
return true
321+
322+
return hasCheckbox
301323
}
302324

303325
private fun updateCheckboxStates(chatMessage: ChatMessage, user: User, checkboxes: List<CheckBox>) {
@@ -332,17 +354,18 @@ class OutcomingTextMessageViewHolder(itemView: View) :
332354
}
333355

334356
private fun updateMessageWithCheckboxStates(originalMessage: String, checkboxes: List<CheckBox>): String {
335-
var updatedMessage = originalMessage
336-
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
337-
338-
checkboxes.forEach { _ ->
339-
updatedMessage = regex.replace(updatedMessage) { matchResult ->
340-
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
341-
val checkboxState = if (checkboxes.find { it.text == taskText }?.isChecked == true) "X" else " "
342-
"- [$checkboxState] $taskText"
357+
val checkboxRegex = """- \[(X|x| )]\s*(.+)""".toRegex()
358+
var index = 0
359+
return originalMessage.lines().joinToString("\n") { line ->
360+
val match = checkboxRegex.matchEntire(line.trim())
361+
if (match != null) {
362+
val taskText = match.groupValues[2].trim()
363+
val state = if (checkboxes.getOrNull(index++)?.isChecked == true) "X" else " "
364+
"- [$state] $taskText"
365+
} else {
366+
line
343367
}
344368
}
345-
return updatedMessage
346369
}
347370

348371
private fun updateStatus(readStatusDrawableInt: Int, description: String?) {
@@ -445,8 +468,6 @@ class OutcomingTextMessageViewHolder(itemView: View) :
445468
companion object {
446469
const val TEXT_SIZE_MULTIPLIER = 2.5
447470
private val TAG = OutcomingTextMessageViewHolder::class.java.simpleName
448-
private const val CHECKED_GROUP_INDEX = 2
449-
private const val TASK_TEXT_GROUP_INDEX = 3
450471
private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000
451472
}
452473
}

app/src/main/res/layout/item_custom_incoming_text_message.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@
7474
android:layout_width="wrap_content"
7575
android:layout_height="wrap_content"
7676
android:orientation="vertical"
77-
android:layout_below="@id/messageText"
7877
android:layout_marginBottom="8dp"
79-
android:visibility="gone">
78+
android:visibility="gone"
79+
app:layout_alignSelf="flex_start"
80+
app:layout_flexGrow="1"
81+
app:layout_wrapBefore="true">
8082
</LinearLayout>
8183

8284
<TextView

app/src/main/res/layout/item_custom_outcoming_text_message.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@
5656
android:layout_height="wrap_content"
5757
android:orientation="vertical"
5858
android:layout_marginBottom="8dp"
59-
android:layout_below="@id/messageText"
60-
android:visibility="gone">
61-
59+
android:visibility="gone"
60+
app:layout_alignSelf="flex_start"
61+
app:layout_flexGrow="1"
62+
app:layout_wrapBefore="true">
6263
</LinearLayout>
6364

6465
<TextView

0 commit comments

Comments
 (0)