mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
attempt to fix ANR when handling markdown
This commit is contained in:
parent
8518ec5802
commit
91e16d3208
2 changed files with 54 additions and 59 deletions
|
|
@ -53,11 +53,13 @@ abstract class BaseTaskViewHolder constructor(
|
|||
private val iconViewChallenge: ImageView? = itemView.findViewById(R.id.iconviewChallenge)
|
||||
private val iconViewReminder: ImageView? = itemView.findViewById(R.id.iconviewReminder)
|
||||
private val taskIconWrapper: LinearLayout? = itemView.findViewById(R.id.taskIconWrapper)
|
||||
private val approvalRequiredTextView: TextView = itemView.findViewById(R.id.approvalRequiredTextField)
|
||||
private val approvalRequiredTextView: TextView =
|
||||
itemView.findViewById(R.id.approvalRequiredTextField)
|
||||
private val expandNotesButton: Button? = itemView.findViewById(R.id.expand_notes_button)
|
||||
private val syncingView: ProgressBar? = itemView.findViewById(R.id.syncing_view)
|
||||
private val errorIconView: ImageButton? = itemView.findViewById(R.id.error_icon)
|
||||
protected val taskGray: Int = ContextCompat.getColor(itemView.context, R.color.offset_background)
|
||||
protected val taskGray: Int =
|
||||
ContextCompat.getColor(itemView.context, R.color.offset_background)
|
||||
protected val streakIconView: ImageView = itemView.findViewById(R.id.iconViewStreak)
|
||||
protected val streakTextView: TextView = itemView.findViewById(R.id.streakTextView)
|
||||
protected val reminderTextView: TextView = itemView.findViewById(R.id.reminder_textview)
|
||||
|
|
@ -115,7 +117,8 @@ abstract class BaseTaskViewHolder constructor(
|
|||
if (ellipses && notesTextView.maxLines != 3) {
|
||||
notesTextView.maxLines = 3
|
||||
}
|
||||
expandNotesButton?.visibility = if (ellipses || notesExpanded) View.VISIBLE else View.GONE
|
||||
expandNotesButton?.visibility =
|
||||
if (ellipses || notesExpanded) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -158,78 +161,58 @@ abstract class BaseTaskViewHolder constructor(
|
|||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (canContainMarkdown()) {
|
||||
if (data.parsedText != null) {
|
||||
titleTextView.setParsedMarkdown(data.parsedText)
|
||||
} else if (MarkdownParser.hasCached(data.text)) {
|
||||
titleTextView.setParsedMarkdown(MarkdownParser.parseMarkdown(data.text))
|
||||
} else {
|
||||
titleTextView.text = data.text
|
||||
if (data.text.isNotEmpty()) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(data.text)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedText = parsedText
|
||||
titleTextView.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
titleTextView.text = data.text
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (data.text.isNotEmpty() && MarkdownParser.containsMarkdown(data.text)) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(data.text)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedText = parsedText
|
||||
titleTextView.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
if (displayMode != "minimal") {
|
||||
when {
|
||||
data.parsedNotes != null -> {
|
||||
notesTextView?.setParsedMarkdown(data.parsedNotes)
|
||||
}
|
||||
if (displayMode != "minimal") {
|
||||
notesTextView?.text = data.notes
|
||||
data.notes?.let { notes ->
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (notes.isEmpty() || !MarkdownParser.containsMarkdown(notes)) {
|
||||
return@launch
|
||||
}
|
||||
MarkdownParser.hasCached(data.notes) -> {
|
||||
notesTextView?.setParsedMarkdown(MarkdownParser.parseMarkdown(data.notes))
|
||||
}
|
||||
else -> {
|
||||
notesTextView?.text = data.notes
|
||||
data.notes?.let { notes ->
|
||||
if (notes.isEmpty()) {
|
||||
return@let
|
||||
}
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val parsedNotes = MarkdownParser.parseMarkdown(notes)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedNotes = parsedNotes
|
||||
notesTextView?.setParsedMarkdown(parsedNotes)
|
||||
}
|
||||
}
|
||||
}
|
||||
val parsedNotes = MarkdownParser.parseMarkdown(notes)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedNotes = parsedNotes
|
||||
notesTextView?.setParsedMarkdown(parsedNotes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
titleTextView.text = data.text
|
||||
if (displayMode != "minimal") {
|
||||
notesTextView?.text = data.notes
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
|
||||
if (displayMode == "standard") {
|
||||
iconViewReminder?.visibility = if ((data.reminders?.size ?: 0) > 0) View.VISIBLE else View.GONE
|
||||
iconViewReminder?.visibility =
|
||||
if ((data.reminders?.size ?: 0) > 0) View.VISIBLE else View.GONE
|
||||
|
||||
iconViewChallenge?.visibility = if (task?.challengeID != null) View.VISIBLE else View.GONE
|
||||
iconViewChallenge?.visibility =
|
||||
if (task?.challengeID != null) View.VISIBLE else View.GONE
|
||||
if (task?.challengeID != null) {
|
||||
if (task?.challengeBroken?.isNotBlank() == true) {
|
||||
iconViewChallenge?.alpha = 1.0f
|
||||
iconViewChallenge?.imageTintList = ContextCompat.getColorStateList(context, R.color.white)
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.white)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_broken_megaphone)
|
||||
} else {
|
||||
iconViewChallenge?.alpha = 0.3f
|
||||
iconViewChallenge?.imageTintList = ContextCompat.getColorStateList(context, R.color.text_ternary)
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.text_ternary)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_megaphone)
|
||||
}
|
||||
}
|
||||
configureSpecialTaskTextView(data)
|
||||
|
||||
iconViewTeam?.visibility = if (data.isGroupTask && ownerID != data.group?.groupID) View.VISIBLE else View.GONE
|
||||
iconViewTeam?.visibility =
|
||||
if (data.isGroupTask && ownerID != data.group?.groupID) View.VISIBLE else View.GONE
|
||||
|
||||
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
|
|
@ -244,7 +227,10 @@ abstract class BaseTaskViewHolder constructor(
|
|||
}
|
||||
|
||||
if (data.group?.assignedUsers?.isNotEmpty() == true) {
|
||||
assignedTextView.text = assignedTextProvider?.assignedTextForTask(context.resources, data.group?.assignedUsers ?: emptyList())
|
||||
assignedTextView.text = assignedTextProvider?.assignedTextForTask(
|
||||
context.resources,
|
||||
data.group?.assignedUsers ?: emptyList()
|
||||
)
|
||||
assignedTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
assignedTextView.visibility = View.GONE
|
||||
|
|
@ -252,7 +238,8 @@ abstract class BaseTaskViewHolder constructor(
|
|||
|
||||
val completedCount = data.group?.assignedUsersDetail?.filter { it.completed }?.size ?: 0
|
||||
if (completedCount > 0) {
|
||||
completedCountTextView.text = "$completedCount/${data?.group?.assignedUsersDetail?.size}"
|
||||
completedCountTextView.text =
|
||||
"$completedCount/${data?.group?.assignedUsersDetail?.size}"
|
||||
completedCountTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
completedCountTextView.visibility = View.GONE
|
||||
|
|
@ -310,10 +297,6 @@ abstract class BaseTaskViewHolder constructor(
|
|||
return true
|
||||
}
|
||||
|
||||
open fun canContainMarkdown(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
open fun setDisabled(openTaskDisabled: Boolean, taskActionsDisabled: Boolean) {
|
||||
this.openTaskDisabled = openTaskDisabled
|
||||
this.taskActionsDisabled = taskActionsDisabled
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.text.SpannableString
|
|||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.util.Linkify
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
import com.habitrpg.common.habitica.R
|
||||
import com.habitrpg.common.habitica.extensions.handleUrlClicks
|
||||
|
|
@ -28,7 +29,10 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.Integer.min
|
||||
import java.lang.NullPointerException
|
||||
import kotlin.system.measureNanoTime
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
object MarkdownParser {
|
||||
private val cache = sortedMapOf<Int, Spanned>()
|
||||
|
|
@ -124,8 +128,11 @@ object MarkdownParser {
|
|||
}
|
||||
|
||||
fun hasCached(input: String?): Boolean {
|
||||
if (input == null) {
|
||||
return false
|
||||
}
|
||||
return try {
|
||||
cache.containsKey(input?.hashCode())
|
||||
cache.containsKey(input.hashCode())
|
||||
} catch (_: NullPointerException) {
|
||||
false
|
||||
}
|
||||
|
|
@ -140,6 +147,11 @@ object MarkdownParser {
|
|||
fun parseCompiled(input: CharSequence): String? {
|
||||
return EmojiParser.convertToCheatCode(input.toString())
|
||||
}
|
||||
|
||||
private val markdownRegex = ".*[\\*#_\\[].*".toRegex()
|
||||
fun containsMarkdown(text: String): Boolean {
|
||||
return text.matches(markdownRegex)
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setMarkdown(input: String?) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue