Skip to content

Commit 1717409

Browse files
authored
Merge pull request #1031 from wordpress-mobile/feature/implement-merging-of-placeholders
Implement merging of placeholders
2 parents b94299d + 91d45dc commit 1717409

File tree

7 files changed

+467
-30
lines changed

7 files changed

+467
-30
lines changed

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

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2161,24 +2161,92 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
21612161
lineBlockFormatter.insertVideo(shouldAddMediaInline, drawable, attributes, onVideoTappedListener, onMediaDeletedListener)
21622162
}
21632163

2164+
fun removeMedia(predicate: (Attributes) -> Boolean) {
2165+
removeMedia(object : AttributePredicate {
2166+
override fun matches(attrs: Attributes): Boolean {
2167+
return predicate(attrs)
2168+
}
2169+
})
2170+
}
2171+
21642172
fun removeMedia(attributePredicate: AttributePredicate) {
21652173
text.getSpans(0, text.length, AztecMediaSpan::class.java)
21662174
.filter {
21672175
attributePredicate.matches(it.attributes)
21682176
}
2169-
.forEach {
2170-
val start = text.getSpanStart(it)
2171-
val end = text.getSpanEnd(it)
2177+
.forEach { mediaSpan ->
2178+
mediaSpan.beforeMediaDeleted()
2179+
val start = text.getSpanStart(mediaSpan)
2180+
val end = text.getSpanEnd(mediaSpan)
21722181

21732182
val clickableSpan = text.getSpans(start, end, AztecMediaClickableSpan::class.java).firstOrNull()
21742183

21752184
text.removeSpan(clickableSpan)
2176-
text.removeSpan(it)
2185+
text.removeSpan(mediaSpan)
2186+
val endPlus1 = (end + 1).coerceAtMost(text.length - 1)
2187+
if (text.length > end + 2 && text[end] == '\n') {
2188+
data class TemporarySpan(
2189+
val span: IAztecBlockSpan,
2190+
val start: Int,
2191+
val end: Int,
2192+
val flags: Int
2193+
)
21772194

2178-
text.delete(start, end)
2195+
val spans = text.getSpans(end, end + 2, IAztecBlockSpan::class.java).map { blockSpan ->
2196+
TemporarySpan(
2197+
blockSpan,
2198+
text.getSpanStart(blockSpan),
2199+
text.getSpanEnd(blockSpan),
2200+
text.getSpanFlags(blockSpan)
2201+
)
2202+
}
2203+
spans.forEach { temporarySpan ->
2204+
text.removeSpan(temporarySpan)
2205+
}
2206+
text.delete(start, endPlus1)
2207+
spans.forEach { temporarySpan ->
2208+
text.setSpan(
2209+
temporarySpan.span,
2210+
(temporarySpan.start - 2).coerceAtLeast(0),
2211+
(temporarySpan.end - 2).coerceAtMost(text.length),
2212+
temporarySpan.flags
2213+
)
2214+
}
2215+
} else {
2216+
text.delete(start, end)
2217+
}
2218+
mediaSpan.onMediaDeleted()
21792219
}
21802220
}
21812221

2222+
fun replaceMediaSpan(aztecMediaSpan: AztecMediaSpan, predicate: (Attributes) -> Boolean) {
2223+
replaceMediaSpan(object : AttributePredicate {
2224+
override fun matches(attrs: Attributes): Boolean {
2225+
return predicate(attrs)
2226+
}
2227+
}, aztecMediaSpan)
2228+
}
2229+
2230+
fun replaceMediaSpan(attributePredicate: AttributePredicate, aztecMediaSpan: AztecMediaSpan) {
2231+
history.beforeTextChanged(this@AztecText)
2232+
text.getSpans(0, text.length, AztecMediaSpan::class.java).firstOrNull {
2233+
attributePredicate.matches(it.attributes)
2234+
}?.let { mediaSpan ->
2235+
mediaSpan.beforeMediaDeleted()
2236+
val start = text.getSpanStart(mediaSpan)
2237+
val end = text.getSpanEnd(mediaSpan)
2238+
2239+
val clickableSpan = text.getSpans(start, end, AztecMediaClickableSpan::class.java).firstOrNull()
2240+
2241+
text.removeSpan(clickableSpan)
2242+
text.removeSpan(mediaSpan)
2243+
mediaSpan.onMediaDeleted()
2244+
aztecMediaSpan.onMediaDeletedListener = onMediaDeletedListener
2245+
lineBlockFormatter.insertMediaSpanOverCurrentChar(aztecMediaSpan, start)
2246+
contentChangeWatcher.notifyContentChanged()
2247+
}
2248+
}
2249+
21822250
interface AttributePredicate {
21832251
/**
21842252
* Return true if the attributes list fulfills some condition

aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,24 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) {
150150
}
151151
}
152152

153+
fun insertMediaSpanOverCurrentChar(span: AztecMediaSpan, position: Int) {
154+
editor.removeInlineStylesFromRange(selectionStart, selectionEnd)
155+
156+
editor.editableText.setSpan(
157+
span,
158+
position,
159+
position + 1,
160+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
161+
)
162+
163+
editor.editableText.setSpan(
164+
AztecMediaClickableSpan(span),
165+
position,
166+
position + 1,
167+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
168+
)
169+
}
170+
153171
private fun insertMediaInline(span: AztecMediaSpan) {
154172
editor.removeInlineStylesFromRange(selectionStart, selectionEnd)
155173

aztec/src/test/kotlin/org/wordpress/aztec/ImageBlockTest.kt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.robolectric.Robolectric
1111
import org.robolectric.RobolectricTestRunner
1212
import org.wordpress.aztec.source.SourceViewEditText
1313
import org.wordpress.aztec.toolbar.AztecToolbar
14+
import org.xml.sax.Attributes
1415

1516
@RunWith(RobolectricTestRunner::class)
1617
class ImageBlockTest {
@@ -211,4 +212,70 @@ class ImageBlockTest {
211212

212213
Assert.assertEquals("<p>Line 1</p><img id=\"1234\" /><p>Line 2</p><p>Line 3</p>", editText.toHtml())
213214
}
215+
216+
@Test
217+
@Throws(Exception::class)
218+
fun addAndRemoveImage() {
219+
val initialHtml = "<p>Line 1</p><p>Line 2</p>"
220+
editText.fromHtml(initialHtml)
221+
222+
editText.setSelection(editText.editableText.indexOf("1") + 2)
223+
val attributes = AztecAttributes()
224+
attributes.setValue("id", "1234")
225+
editText.insertImage(null, attributes)
226+
227+
Assert.assertEquals("<p>Line 1</p><img id=\"1234\" /><p>Line 2</p>", editText.toHtml())
228+
229+
editText.removeMedia(object : AztecText.AttributePredicate {
230+
override fun matches(attrs: Attributes): Boolean {
231+
return attrs.getValue("id") == "1234"
232+
}
233+
})
234+
235+
Assert.assertEquals(initialHtml, editText.toHtml())
236+
}
237+
238+
@Test
239+
@Throws(Exception::class)
240+
fun addAndRemoveImageAtTheBeginning() {
241+
val initialHtml = "Line 1"
242+
editText.fromHtml(initialHtml)
243+
244+
editText.setSelection(0)
245+
val attributes = AztecAttributes()
246+
attributes.setValue("id", "1234")
247+
editText.insertImage(null, attributes)
248+
249+
Assert.assertEquals("<img id=\"1234\" />Line 1", editText.toHtml())
250+
251+
editText.removeMedia(object : AztecText.AttributePredicate {
252+
override fun matches(attrs: Attributes): Boolean {
253+
return attrs.getValue("id") == "1234"
254+
}
255+
})
256+
257+
Assert.assertEquals(initialHtml, editText.toHtml())
258+
}
259+
260+
@Test
261+
@Throws(Exception::class)
262+
fun addAndRemoveImageAtTheBeginningOfPlaceholder() {
263+
val initialHtml = "<p>Line 1</p>"
264+
editText.fromHtml(initialHtml)
265+
266+
editText.setSelection(0)
267+
val attributes = AztecAttributes()
268+
attributes.setValue("id", "1234")
269+
editText.insertImage(null, attributes)
270+
271+
Assert.assertEquals("<img id=\"1234\" /><p>Line 1</p>", editText.toHtml())
272+
273+
editText.removeMedia(object : AztecText.AttributePredicate {
274+
override fun matches(attrs: Attributes): Boolean {
275+
return attrs.getValue("id") == "1234"
276+
}
277+
})
278+
279+
Assert.assertEquals(initialHtml, editText.toHtml())
280+
}
214281
}

media-placeholders/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
implementation 'androidx.appcompat:appcompat:1.0.0'
4040
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
4141
testImplementation "junit:junit:$jUnitVersion"
42+
testImplementation "org.robolectric:robolectric:$robolectricVersion"
4243
}
4344

4445
project.afterEvaluate {

media-placeholders/src/main/java/org/wordpress/aztec/placeholders/ImageWithCaptionAdapter.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,19 @@ class ImageWithCaptionAdapter(
8282
private const val SRC_ATTRIBUTE = "src"
8383

8484
suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String) {
85-
placeholderManager.insertItem(ADAPTER_TYPE, SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to caption)
85+
placeholderManager.insertOrUpdateItem(ADAPTER_TYPE) { currentAttributes, type, placeAtStart ->
86+
if (currentAttributes == null || type != ADAPTER_TYPE) {
87+
mapOf(SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to caption)
88+
} else {
89+
val currentCaption = currentAttributes[CAPTION_ATTRIBUTE]
90+
val newCaption = if (placeAtStart) {
91+
"$caption - $currentCaption"
92+
} else {
93+
"$currentCaption - $caption"
94+
}
95+
mapOf(SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to newCaption)
96+
}
97+
}
8698
}
8799
}
88100
}

0 commit comments

Comments
 (0)