Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2275,15 +2275,13 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
text.getSpans(0, text.length, AztecMediaSpan::class.java).firstOrNull {
attributePredicate.matches(it.attributes)
}?.let { mediaSpan ->
mediaSpan.beforeMediaDeleted()
val start = text.getSpanStart(mediaSpan)
val end = text.getSpanEnd(mediaSpan)

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

text.removeSpan(clickableSpan)
text.removeSpan(mediaSpan)
mediaSpan.onMediaDeleted()
aztecMediaSpan.onMediaDeletedListener = onMediaDeletedListener
lineBlockFormatter.insertMediaSpanOverCurrentChar(aztecMediaSpan, start)
contentChangeWatcher.notifyContentChanged()
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ ext {

ext {
gradlePluginVersion = '3.3.1'
kotlinCoroutinesVersion = '1.1.0'
kotlinCoroutinesVersion = '1.6.4'
tagSoupVersion = '1.2.1'
glideVersion = '4.10.0'
picassoVersion = '2.5.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ class ImageWithCaptionAdapter(
private const val CAPTION_ATTRIBUTE = "caption"
private const val SRC_ATTRIBUTE = "src"

suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String) {
placeholderManager.insertOrUpdateItem(ADAPTER_TYPE) { currentAttributes, type, placeAtStart ->
suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String, shouldMergePlaceholders: Boolean = true) {
placeholderManager.insertOrUpdateItem(ADAPTER_TYPE, {
shouldMergePlaceholders
}) { currentAttributes, type, placeAtStart ->
if (currentAttributes == null || type != ADAPTER_TYPE) {
mapOf(SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to caption)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,19 @@ class PlaceholderManager(
val targetItem = getTargetItem()
val targetSpan = targetItem?.span
val currentType = targetSpan?.attributes?.getValue(TYPE_ATTRIBUTE)
if (currentType != null && shouldMergeItem(currentType)) {
updateSpan(type, targetItem.span, targetItem.placeAtStart, updateItem, currentType)
if (currentType != null) {
if (shouldMergeItem(currentType)) {
updateSpan(type, targetItem.span, targetItem.placeAtStart, updateItem, currentType)
} else {
val (newLinePosition, targetSelection) = if (targetItem.placeAtStart) {
targetItem.spanStart to targetItem.spanStart
} else {
targetItem.spanEnd to targetItem.spanEnd + 2
}
aztecText.text.insert(newLinePosition, Constants.NEWLINE_STRING)
aztecText.setSelection(targetSelection)
insertItem(type, *updateItem(null, null, false).toList().toTypedArray())
}
} else {
insertItem(type, *updateItem(null, null, false).toList().toTypedArray())
}
Expand Down Expand Up @@ -192,7 +203,7 @@ class PlaceholderManager(
return true
}

private data class TargetItem(val span: AztecPlaceholderSpan, val placeAtStart: Boolean)
private data class TargetItem(val span: AztecPlaceholderSpan, val placeAtStart: Boolean, val spanStart: Int, val spanEnd: Int)

private fun getTargetItem(): TargetItem? {
if (aztecText.length() == 0) {
Expand Down Expand Up @@ -224,7 +235,7 @@ class PlaceholderManager(
from,
to,
AztecPlaceholderSpan::class.java
).map { TargetItem(it, placeAtStart) }.lastOrNull()
).map { TargetItem(it, placeAtStart, editableText.getSpanStart(it), editableText.getSpanEnd(it)) }.lastOrNull()
}

/**
Expand Down Expand Up @@ -286,7 +297,6 @@ class PlaceholderManager(
}
}
val targetPosition = aztecText.getElementPosition(predicate) ?: return

insertInPosition(aztecAttributes ?: return, targetPosition)
}

Expand Down Expand Up @@ -318,39 +328,51 @@ class PlaceholderManager(
parentTextViewRect.top += parentTextViewTopAndBottomOffset
parentTextViewRect.bottom = parentTextViewRect.top + height

positionToIdMutex.withLock {
positionToId.removeAll {
it.uuid == uuid
var box = container.findViewWithTag<View>(uuid)?.apply {
id = uuid.hashCode()
}
val newWidth = adapter.calculateWidth(attrs, windowWidth) - EDITOR_INNER_PADDING
val newHeight = height - EDITOR_INNER_PADDING
val padding = 10
val newLeftPadding = parentTextViewRect.left + padding + aztecText.paddingStart
val newTopPadding = parentTextViewRect.top + padding
box?.let { existingView ->
val currentParams = existingView.layoutParams as FrameLayout.LayoutParams
val widthSame = currentParams.width == newWidth
val heightSame = currentParams.height == newHeight
val topMarginSame = currentParams.topMargin == newTopPadding
val leftMarginSame = currentParams.leftMargin == newLeftPadding
if (widthSame && heightSame && topMarginSame && leftMarginSame) {
return
}
container.removeView(box)
positionToIdMutex.withLock {
positionToId.removeAll {
it.uuid == uuid
}
}
}
box = adapter.createView(container.context, uuid, attrs)

var box = container.findViewWithTag<View>(uuid)
val exists = box != null
if (!exists) {
box = adapter.createView(container.context, uuid, attrs)
}
val params = FrameLayout.LayoutParams(
adapter.calculateWidth(attrs, windowWidth) - EDITOR_INNER_PADDING,
height - EDITOR_INNER_PADDING
)
val padding = 10
params.setMargins(
parentTextViewRect.left + padding + aztecText.paddingStart,
parentTextViewRect.top + padding,
0,
0
)
box.layoutParams = params
box.tag = uuid
box.id = uuid.hashCode()
box.setBackgroundColor(Color.TRANSPARENT)
box.setOnTouchListener(adapter)
box.tag = uuid
box.layoutParams = FrameLayout.LayoutParams(
newWidth,
newHeight
).apply {
leftMargin = newLeftPadding
topMargin = newTopPadding
}

positionToIdMutex.withLock {
positionToId.add(Placeholder(targetPosition, uuid))
}
if (!exists && box.parent == null) {
if (box.parent == null) {
container.addView(box)
adapter.onViewCreated(box, uuid)
}
adapter.onViewCreated(box, uuid)
}

private fun validateAttributes(attributes: AztecAttributes): Boolean {
Expand Down Expand Up @@ -418,7 +440,13 @@ class PlaceholderManager(
/**
* This method handled a `placeholder` tag found in the HTML. It creates a placeholder and inserts a view over it.
*/
override fun handleTag(opening: Boolean, tag: String, output: Editable, attributes: Attributes, nestingLevel: Int): Boolean {
override fun handleTag(
opening: Boolean,
tag: String,
output: Editable,
attributes: Attributes,
nestingLevel: Int
): Boolean {
if (opening) {
val type = attributes.getValue(TYPE_ATTRIBUTE)
attributes.getValue(UUID_ATTRIBUTE)?.also { uuid ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class PlaceholderTest {
lateinit var toolbar: AztecToolbar
lateinit var placeholderManager: PlaceholderManager

private val uuid: String = "uuid123"
private val uuid1: String = "uuid1"
private val uuid2: String = "uuid2"

/**
* Initialize variables.
Expand All @@ -38,8 +39,9 @@ class PlaceholderTest {
container = FrameLayout(activity)
editText = AztecText(activity)
container.addView(editText, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
var counter = 0
placeholderManager = PlaceholderManager(editText, container, generateUuid = {
uuid
listOf(uuid1, uuid2)[counter++]
})
placeholderManager.registerAdapter(ImageWithCaptionAdapter())
editText.setCalypsoMode(false)
Expand All @@ -65,10 +67,10 @@ class PlaceholderTest {
editText.setSelection(0)
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 123")

Assert.assertEquals("<placeholder uuid=\"$uuid\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 123\" /><p>Line 1</p>", editText.toHtml())
Assert.assertEquals("<placeholder uuid=\"$uuid1\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 123\" /><p>Line 1</p>", editText.toHtml())

placeholderManager.removeItem {
it.getValue("uuid") == uuid
it.getValue("uuid") == uuid1
}

Assert.assertEquals(initialHtml, editText.toHtml())
Expand All @@ -85,10 +87,10 @@ class PlaceholderTest {
editText.setSelection(editText.editableText.indexOf("1"))
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 123")

Assert.assertEquals("<p>Line 123<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 123\" /></p><p>Line 2</p>", editText.toHtml())
Assert.assertEquals("<p>Line 123<placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 123\" /></p><p>Line 2</p>", editText.toHtml())

placeholderManager.removeItem {
it.getValue("uuid") == uuid
it.getValue("uuid") == uuid1
}

Assert.assertEquals(initialHtml, editText.toHtml())
Expand All @@ -97,7 +99,7 @@ class PlaceholderTest {

@Test
@Throws(Exception::class)
fun insertOrUpdateAPlaceholderAtTheBeginning() {
fun updatePlaceholderAtTheBeginning() {
runBlocking {
val initialHtml = "<p>Line 1</p>"
editText.fromHtml(initialHtml)
Expand All @@ -109,7 +111,7 @@ class PlaceholderTest {
Assert.assertEquals("${placeholderWithCaption("Caption 1 - Caption 2")}<p>Line 1</p>", editText.toHtml())

placeholderManager.removeItem {
it.getValue("uuid") == uuid
it.getValue("uuid") == uuid1
}

Assert.assertEquals(initialHtml, editText.toHtml())
Expand All @@ -118,7 +120,28 @@ class PlaceholderTest {

@Test
@Throws(Exception::class)
fun insertOrUpdateAPlaceholderWhenInsertingBeforeNewLine() {
fun doNotUpdatePlaceholderAtTheBeginningWhenMergeDisabled() {
runBlocking {
val initialHtml = "<p>Line 1</p>"
editText.fromHtml(initialHtml)

editText.setSelection(0)
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 1")
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 2", shouldMergePlaceholders = false)

Assert.assertEquals("<placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 1\" /><br><placeholder uuid=\"uuid2\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 2\" /><p>Line 1</p>", editText.toHtml())

placeholderManager.removeItem {
it.getValue("uuid") == uuid1
}

Assert.assertEquals("<br><placeholder uuid=\"uuid2\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 2\" /><p>Line 1</p>", editText.toHtml())
}
}

@Test
@Throws(Exception::class)
fun updatePlaceholderWhenInsertingBeforeNewLine() {
runBlocking {
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
editText.fromHtml(initialHtml)
Expand All @@ -132,7 +155,21 @@ class PlaceholderTest {

@Test
@Throws(Exception::class)
fun insertOrUpdateAPlaceholderWhenInsertingRightBefore() {
fun doNotUpdatePlaceholderWhenInsertingBeforeNewLineAndMergeDisabled() {
runBlocking {
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
editText.fromHtml(initialHtml)

editText.setSelection(editText.editableText.indexOf("1"))
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Second", shouldMergePlaceholders = false)

Assert.assertEquals("<p>Line 1</p><placeholder uuid=\"uuid2\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Second\" /><br><placeholder src=\"image.jpg\" caption=\"First\" uuid=\"uuid1\" type=\"image_with_caption\" /><p>Line 2</p>", editText.toHtml())
}
}

@Test
@Throws(Exception::class)
fun updatePlaceholderWhenInsertingRightBefore() {
runBlocking {
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
editText.fromHtml(initialHtml)
Expand All @@ -144,18 +181,32 @@ class PlaceholderTest {
}
}

@Test
@Throws(Exception::class)
fun doNotUpdatePlaceholderWhenInsertingRightBeforeAndMergeDisabled() {
runBlocking {
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
editText.fromHtml(initialHtml)

editText.setSelection(editText.editableText.indexOf("1") + 1)
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Second", shouldMergePlaceholders = false)

Assert.assertEquals("<p>Line 1</p><placeholder uuid=\"uuid2\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Second\" /><br>${placeholderWithCaption("First")}<p>Line 2</p>", editText.toHtml())
}
}

private fun placeholderWithCaption(caption: String): String {
return "<placeholder src=\"image.jpg\" caption=\"$caption\" uuid=\"uuid123\" type=\"image_with_caption\" />"
return "<placeholder src=\"image.jpg\" caption=\"$caption\" uuid=\"uuid1\" type=\"image_with_caption\" />"
}

@Test
@Throws(Exception::class)
fun updatePlaceholderWhenItShouldBe() {
runBlocking {
val initialHtml = "<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
val initialHtml = "<placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
editText.fromHtml(initialHtml)

placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
true
}) { currentAttributes ->
val result = mutableMapOf<String, String>()
Expand All @@ -164,21 +215,21 @@ class PlaceholderTest {
result
}

Assert.assertEquals("<placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid123\" type=\"image_with_caption\" /><p>Line</p>", editText.toHtml())
Assert.assertEquals("<placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid1\" type=\"image_with_caption\" /><p>Line</p>", editText.toHtml())
}
}

@Test
@Throws(Exception::class)
fun updatePlaceholderAtTheEnd() {
runBlocking {
val initialHtml = "<p>First Line</p><placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Second Line</p>"
val initialHtml = "<p>First Line</p><placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Second Line</p>"
editText.fromHtml(initialHtml)
editText.setSelection(editText.editableText.indexOf("First") + 1)
val initialSelectionStart = editText.selectionStart
val initialSelectionEnd = editText.selectionEnd

placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
true
}) { currentAttributes ->
val result = mutableMapOf<String, String>()
Expand All @@ -187,7 +238,7 @@ class PlaceholderTest {
result
}

Assert.assertEquals("<p>First Line</p><placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid123\" type=\"image_with_caption\" /><p>Second Line</p>", editText.toHtml())
Assert.assertEquals("<p>First Line</p><placeholder src=\"image.jpg\" caption=\"Updated caption\" uuid=\"uuid1\" type=\"image_with_caption\" /><p>Second Line</p>", editText.toHtml())
Assert.assertEquals(initialSelectionStart, editText.selectionStart)
Assert.assertEquals(initialSelectionEnd, editText.selectionEnd)
}
Expand All @@ -197,10 +248,10 @@ class PlaceholderTest {
@Throws(Exception::class)
fun removePlaceholderWhenItShouldNotBeUpdated() {
runBlocking {
val initialHtml = "<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
val initialHtml = "<placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
editText.fromHtml(initialHtml)

placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
false
}) { currentAttributes ->
val result = mutableMapOf<String, String>()
Expand Down