Skip to content

Commit cbb43e3

Browse files
authored
Merge pull request #1092 from wordpress-mobile/feature/improve-performance-of-to-html-method
Improve performance of toHtml method by moving it to background thread
2 parents 0c56aec + 600046e commit cbb43e3

File tree

7 files changed

+145
-7
lines changed

7 files changed

+145
-7
lines changed

aztec/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ dependencies {
5959

6060
testImplementation "junit:junit:$jUnitVersion"
6161
testImplementation "org.robolectric:robolectric:$robolectricVersion"
62-
testImplementation 'androidx.test:core:1.4.0'
62+
testImplementation 'androidx.test:core:1.6.1'
6363

6464
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion"
6565
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
66+
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion"
6667

6768
implementation 'org.apache.commons:commons-lang3:3.8.1'
6869

aztec/src/main/kotlin/org/wordpress/aztec/Aztec.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ open class Aztec private constructor(
7878
}
7979

8080
@JvmStatic
81-
fun with(visualEditor: AztecText, toolbar: AztecToolbar, toolbarClickListener: IAztecToolbarClickListener): Aztec {
81+
fun with(visualEditor: AztecText, toolbar: IAztecToolbar, toolbarClickListener: IAztecToolbarClickListener): Aztec {
8282
return Aztec(visualEditor, null, toolbar, toolbarClickListener)
8383
}
8484
}

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,24 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
429429
return super.getText()!!
430430
}
431431

432+
/**
433+
* The getText method returns mutable version of the content. This means that it can change
434+
* when being worked on. Call this method when you want your Editable to be immutable.
435+
*/
436+
fun getTextCopy(): Editable {
437+
val copy = SpannableStringBuilder(text.toString())
438+
439+
val spans: Array<Any> = text.getSpans(0, text.length, Any::class.java)
440+
441+
for (span in spans) {
442+
val spanStart = text.getSpanStart(span)
443+
val spanEnd = text.getSpanEnd(span)
444+
val flags = text.getSpanFlags(span)
445+
copy.setSpan(span, spanStart, spanEnd, flags)
446+
}
447+
return copy
448+
}
449+
432450
@SuppressLint("ResourceType")
433451
private fun init(attrs: AttributeSet?) {
434452
disableTextChangedListener()
@@ -1631,6 +1649,10 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
16311649
return toHtml(text, withCursorTag)
16321650
}
16331651

1652+
suspend fun toHtmlAsync(withCursorTag: Boolean = false): String {
1653+
return toHtmlAsync(getTextCopy(), withCursorTag)
1654+
}
1655+
16341656
// general function accepts any Spannable and converts it to regular or "calypso" html
16351657
// depending on the mode
16361658
fun toHtml(content: Spannable, withCursorTag: Boolean = false): String {
@@ -1645,6 +1667,10 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
16451667
}
16461668
}
16471669

1670+
suspend fun toHtmlAsync(content: Editable, withCursorTag: Boolean = false): String {
1671+
return toPlainHtmlAsync(content, withCursorTag)
1672+
}
1673+
16481674
// platform agnostic HTML
16491675
// default behavior returns HTML from this text
16501676
fun toPlainHtml(withCursorTag: Boolean = false): String {
@@ -1664,6 +1690,12 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
16641690
}
16651691
}
16661692

1693+
suspend fun toPlainHtmlAsync(content: Editable, withCursorTag: Boolean = false): String {
1694+
return withContext(Dispatchers.Default) {
1695+
parseHtml(content, withCursorTag)
1696+
}
1697+
}
1698+
16671699
private fun parseHtml(content: Spannable, withCursorTag: Boolean): String {
16681700
val parser = AztecParser(alignmentRendering, plugins)
16691701
val output: SpannableStringBuilder
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.wordpress.aztec
2+
3+
import android.app.Activity
4+
import android.view.MenuItem
5+
import android.widget.PopupMenu
6+
import android.widget.ToggleButton
7+
import kotlinx.coroutines.Job
8+
import kotlinx.coroutines.launch
9+
import kotlinx.coroutines.test.runTest
10+
import org.junit.Assert
11+
import org.junit.Before
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
import org.robolectric.Robolectric
15+
import org.robolectric.RobolectricTestRunner
16+
import org.wordpress.aztec.source.SourceViewEditText
17+
import org.wordpress.aztec.toolbar.AztecToolbar
18+
import org.wordpress.aztec.toolbar.ToolbarAction
19+
import org.wordpress.aztec.toolbar.ToolbarItems
20+
21+
@RunWith(RobolectricTestRunner::class)
22+
class AsyncAztecTest {
23+
lateinit var editText: AztecText
24+
lateinit var sourceText: SourceViewEditText
25+
lateinit var toolbar: AztecToolbar
26+
lateinit var buttonQuote: ToggleButton
27+
lateinit var menuHeading: PopupMenu
28+
lateinit var menuHeading1: MenuItem
29+
lateinit var menuHeading2: MenuItem
30+
lateinit var menuParagraph: MenuItem
31+
lateinit var buttonPreformat: ToggleButton
32+
lateinit var buttonBold: ToggleButton
33+
34+
/**
35+
* Initialize variables.
36+
*/
37+
@Before
38+
fun init() {
39+
val activity = Robolectric.buildActivity(Activity::class.java).create().visible().get()
40+
editText = AztecText(activity)
41+
editText.setCalypsoMode(false)
42+
sourceText = SourceViewEditText(activity)
43+
sourceText.setCalypsoMode(false)
44+
toolbar = AztecToolbar(activity)
45+
toolbar.setToolbarItems(
46+
ToolbarItems.BasicLayout(
47+
ToolbarAction.HEADING,
48+
ToolbarAction.PREFORMAT,
49+
ToolbarAction.LIST,
50+
ToolbarAction.QUOTE,
51+
ToolbarAction.BOLD,
52+
ToolbarAction.ITALIC,
53+
ToolbarAction.LINK,
54+
ToolbarAction.UNDERLINE,
55+
ToolbarAction.STRIKETHROUGH,
56+
ToolbarAction.ALIGN_LEFT,
57+
ToolbarAction.ALIGN_CENTER,
58+
ToolbarAction.ALIGN_RIGHT,
59+
ToolbarAction.HORIZONTAL_RULE,
60+
ToolbarAction.HTML
61+
))
62+
toolbar.setEditor(editText, sourceText)
63+
buttonQuote = toolbar.findViewById<ToggleButton>(R.id.format_bar_button_quote)
64+
menuHeading = toolbar.getHeadingMenu() as PopupMenu
65+
menuHeading1 = menuHeading.menu.getItem(1)
66+
menuHeading2 = menuHeading.menu.getItem(2)
67+
menuParagraph = menuHeading.menu.getItem(0)
68+
buttonPreformat = toolbar.findViewById<ToggleButton>(R.id.format_bar_button_pre)
69+
buttonBold = toolbar.findViewById<ToggleButton>(R.id.format_bar_button_bold)
70+
activity.setContentView(editText)
71+
}
72+
73+
@Test
74+
@Throws(Exception::class)
75+
fun asyncToHtmlWorksOnCurrentVersion() = runTest {
76+
editText.append("One two three")
77+
val textCopy = editText.getTextCopy()
78+
Assert.assertEquals("One two three", editText.toHtmlAsync(textCopy))
79+
val jobs = mutableListOf<Job>()
80+
jobs.add(launch {
81+
Assert.assertEquals("One two three", editText.toHtmlAsync(textCopy))
82+
})
83+
toolbar.onMenuItemClick(menuHeading1)
84+
val textCopy2 = editText.getTextCopy()
85+
jobs.add(launch {
86+
Assert.assertEquals("<h1>One two three</h1>", editText.toHtmlAsync(textCopy2))
87+
})
88+
toolbar.onMenuItemClick(menuParagraph)
89+
val textCopy3 = editText.getTextCopy()
90+
jobs.add(launch {
91+
Assert.assertEquals("One two three", editText.toHtmlAsync(textCopy3))
92+
})
93+
val from = editText.editableText.indexOf("two")
94+
editText.setSelection(from, from + 3)
95+
editText.toggleFormatting(AztecTextFormat.FORMAT_BOLD)
96+
val textCopy4 = editText.getTextCopy()
97+
jobs.add(launch {
98+
Assert.assertEquals("One <strong>two</strong> three", editText.toHtmlAsync(textCopy4))
99+
})
100+
jobs.forEach {
101+
it.join()
102+
Assert.assertTrue(it.isCompleted)
103+
}
104+
}
105+
}

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ ext {
7373
ext {
7474
// mixed
7575
gradlePluginVersion = '3.3.1'
76-
kotlinCoroutinesVersion = '1.6.4'
76+
kotlinCoroutinesVersion = '1.8.1'
7777
tagSoupVersion = '1.2.1'
7878
glideVersion = '4.10.0'
7979
picassoVersion = '2.5.2'
80-
robolectricVersion = '4.11'
81-
jUnitVersion = '4.12'
80+
robolectricVersion = '4.13'
81+
jUnitVersion = '4.13.2'
8282
jSoupVersion = '1.15.3'
8383
wordpressUtilsVersion = '3.5.0'
8484
espressoVersion = '3.0.1'

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pluginManagement {
22
gradle.ext.kotlinVersion = '1.9.24'
3-
gradle.ext.agpVersion = '8.1.0'
3+
gradle.ext.agpVersion = '8.5.0'
44
gradle.ext.automatticPublishToS3Version = '0.8.0'
55
gradle.ext.dependencyAnalysisVersion = '1.33.0'
66

0 commit comments

Comments
 (0)