Skip to content

Commit ed7799b

Browse files
authored
Merge pull request #1047 from wordpress-mobile/feature/improvements-to-placeholder-api
Animate size changes of placeholders
2 parents c51283d + 3a3a158 commit ed7799b

File tree

5 files changed

+132
-53
lines changed

5 files changed

+132
-53
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,15 +2275,13 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
22752275
text.getSpans(0, text.length, AztecMediaSpan::class.java).firstOrNull {
22762276
attributePredicate.matches(it.attributes)
22772277
}?.let { mediaSpan ->
2278-
mediaSpan.beforeMediaDeleted()
22792278
val start = text.getSpanStart(mediaSpan)
22802279
val end = text.getSpanEnd(mediaSpan)
22812280

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

22842283
text.removeSpan(clickableSpan)
22852284
text.removeSpan(mediaSpan)
2286-
mediaSpan.onMediaDeleted()
22872285
aztecMediaSpan.onMediaDeletedListener = onMediaDeletedListener
22882286
lineBlockFormatter.insertMediaSpanOverCurrentChar(aztecMediaSpan, start)
22892287
contentChangeWatcher.notifyContentChanged()

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ ext {
7070

7171
ext {
7272
gradlePluginVersion = '3.3.1'
73-
kotlinCoroutinesVersion = '1.1.0'
73+
kotlinCoroutinesVersion = '1.6.4'
7474
tagSoupVersion = '1.2.1'
7575
glideVersion = '4.10.0'
7676
picassoVersion = '2.5.2'

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ class ImageWithCaptionAdapter(
8181
private const val CAPTION_ATTRIBUTE = "caption"
8282
private const val SRC_ATTRIBUTE = "src"
8383

84-
suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String) {
85-
placeholderManager.insertOrUpdateItem(ADAPTER_TYPE) { currentAttributes, type, placeAtStart ->
84+
suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String, shouldMergePlaceholders: Boolean = true) {
85+
placeholderManager.insertOrUpdateItem(ADAPTER_TYPE, {
86+
shouldMergePlaceholders
87+
}) { currentAttributes, type, placeAtStart ->
8688
if (currentAttributes == null || type != ADAPTER_TYPE) {
8789
mapOf(SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to caption)
8890
} else {

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

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,19 @@ class PlaceholderManager(
123123
val targetItem = getTargetItem()
124124
val targetSpan = targetItem?.span
125125
val currentType = targetSpan?.attributes?.getValue(TYPE_ATTRIBUTE)
126-
if (currentType != null && shouldMergeItem(currentType)) {
127-
updateSpan(type, targetItem.span, targetItem.placeAtStart, updateItem, currentType)
126+
if (currentType != null) {
127+
if (shouldMergeItem(currentType)) {
128+
updateSpan(type, targetItem.span, targetItem.placeAtStart, updateItem, currentType)
129+
} else {
130+
val (newLinePosition, targetSelection) = if (targetItem.placeAtStart) {
131+
targetItem.spanStart to targetItem.spanStart
132+
} else {
133+
targetItem.spanEnd to targetItem.spanEnd + 2
134+
}
135+
aztecText.text.insert(newLinePosition, Constants.NEWLINE_STRING)
136+
aztecText.setSelection(targetSelection)
137+
insertItem(type, *updateItem(null, null, false).toList().toTypedArray())
138+
}
128139
} else {
129140
insertItem(type, *updateItem(null, null, false).toList().toTypedArray())
130141
}
@@ -192,7 +203,7 @@ class PlaceholderManager(
192203
return true
193204
}
194205

195-
private data class TargetItem(val span: AztecPlaceholderSpan, val placeAtStart: Boolean)
206+
private data class TargetItem(val span: AztecPlaceholderSpan, val placeAtStart: Boolean, val spanStart: Int, val spanEnd: Int)
196207

197208
private fun getTargetItem(): TargetItem? {
198209
if (aztecText.length() == 0) {
@@ -224,7 +235,7 @@ class PlaceholderManager(
224235
from,
225236
to,
226237
AztecPlaceholderSpan::class.java
227-
).map { TargetItem(it, placeAtStart) }.lastOrNull()
238+
).map { TargetItem(it, placeAtStart, editableText.getSpanStart(it), editableText.getSpanEnd(it)) }.lastOrNull()
228239
}
229240

230241
/**
@@ -286,7 +297,6 @@ class PlaceholderManager(
286297
}
287298
}
288299
val targetPosition = aztecText.getElementPosition(predicate) ?: return
289-
290300
insertInPosition(aztecAttributes ?: return, targetPosition)
291301
}
292302

@@ -318,39 +328,51 @@ class PlaceholderManager(
318328
parentTextViewRect.top += parentTextViewTopAndBottomOffset
319329
parentTextViewRect.bottom = parentTextViewRect.top + height
320330

321-
positionToIdMutex.withLock {
322-
positionToId.removeAll {
323-
it.uuid == uuid
331+
var box = container.findViewWithTag<View>(uuid)?.apply {
332+
id = uuid.hashCode()
333+
}
334+
val newWidth = adapter.calculateWidth(attrs, windowWidth) - EDITOR_INNER_PADDING
335+
val newHeight = height - EDITOR_INNER_PADDING
336+
val padding = 10
337+
val newLeftPadding = parentTextViewRect.left + padding + aztecText.paddingStart
338+
val newTopPadding = parentTextViewRect.top + padding
339+
box?.let { existingView ->
340+
val currentParams = existingView.layoutParams as FrameLayout.LayoutParams
341+
val widthSame = currentParams.width == newWidth
342+
val heightSame = currentParams.height == newHeight
343+
val topMarginSame = currentParams.topMargin == newTopPadding
344+
val leftMarginSame = currentParams.leftMargin == newLeftPadding
345+
if (widthSame && heightSame && topMarginSame && leftMarginSame) {
346+
return
347+
}
348+
container.removeView(box)
349+
positionToIdMutex.withLock {
350+
positionToId.removeAll {
351+
it.uuid == uuid
352+
}
324353
}
325354
}
355+
box = adapter.createView(container.context, uuid, attrs)
326356

327-
var box = container.findViewWithTag<View>(uuid)
328-
val exists = box != null
329-
if (!exists) {
330-
box = adapter.createView(container.context, uuid, attrs)
331-
}
332-
val params = FrameLayout.LayoutParams(
333-
adapter.calculateWidth(attrs, windowWidth) - EDITOR_INNER_PADDING,
334-
height - EDITOR_INNER_PADDING
335-
)
336-
val padding = 10
337-
params.setMargins(
338-
parentTextViewRect.left + padding + aztecText.paddingStart,
339-
parentTextViewRect.top + padding,
340-
0,
341-
0
342-
)
343-
box.layoutParams = params
344-
box.tag = uuid
357+
box.id = uuid.hashCode()
345358
box.setBackgroundColor(Color.TRANSPARENT)
346359
box.setOnTouchListener(adapter)
360+
box.tag = uuid
361+
box.layoutParams = FrameLayout.LayoutParams(
362+
newWidth,
363+
newHeight
364+
).apply {
365+
leftMargin = newLeftPadding
366+
topMargin = newTopPadding
367+
}
368+
347369
positionToIdMutex.withLock {
348370
positionToId.add(Placeholder(targetPosition, uuid))
349371
}
350-
if (!exists && box.parent == null) {
372+
if (box.parent == null) {
351373
container.addView(box)
352-
adapter.onViewCreated(box, uuid)
353374
}
375+
adapter.onViewCreated(box, uuid)
354376
}
355377

356378
private fun validateAttributes(attributes: AztecAttributes): Boolean {
@@ -418,7 +440,13 @@ class PlaceholderManager(
418440
/**
419441
* This method handled a `placeholder` tag found in the HTML. It creates a placeholder and inserts a view over it.
420442
*/
421-
override fun handleTag(opening: Boolean, tag: String, output: Editable, attributes: Attributes, nestingLevel: Int): Boolean {
443+
override fun handleTag(
444+
opening: Boolean,
445+
tag: String,
446+
output: Editable,
447+
attributes: Attributes,
448+
nestingLevel: Int
449+
): Boolean {
422450
if (opening) {
423451
val type = attributes.getValue(TYPE_ATTRIBUTE)
424452
attributes.getValue(UUID_ATTRIBUTE)?.also { uuid ->

media-placeholders/src/test/java/org/wordpress/aztec/placeholders/PlaceholderTest.kt

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class PlaceholderTest {
2727
lateinit var toolbar: AztecToolbar
2828
lateinit var placeholderManager: PlaceholderManager
2929

30-
private val uuid: String = "uuid123"
30+
private val uuid1: String = "uuid1"
31+
private val uuid2: String = "uuid2"
3132

3233
/**
3334
* Initialize variables.
@@ -38,8 +39,9 @@ class PlaceholderTest {
3839
container = FrameLayout(activity)
3940
editText = AztecText(activity)
4041
container.addView(editText, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
42+
var counter = 0
4143
placeholderManager = PlaceholderManager(editText, container, generateUuid = {
42-
uuid
44+
listOf(uuid1, uuid2)[counter++]
4345
})
4446
placeholderManager.registerAdapter(ImageWithCaptionAdapter())
4547
editText.setCalypsoMode(false)
@@ -65,10 +67,10 @@ class PlaceholderTest {
6567
editText.setSelection(0)
6668
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 123")
6769

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

7072
placeholderManager.removeItem {
71-
it.getValue("uuid") == uuid
73+
it.getValue("uuid") == uuid1
7274
}
7375

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

88-
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())
90+
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())
8991

9092
placeholderManager.removeItem {
91-
it.getValue("uuid") == uuid
93+
it.getValue("uuid") == uuid1
9294
}
9395

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

98100
@Test
99101
@Throws(Exception::class)
100-
fun insertOrUpdateAPlaceholderAtTheBeginning() {
102+
fun updatePlaceholderAtTheBeginning() {
101103
runBlocking {
102104
val initialHtml = "<p>Line 1</p>"
103105
editText.fromHtml(initialHtml)
@@ -109,7 +111,7 @@ class PlaceholderTest {
109111
Assert.assertEquals("${placeholderWithCaption("Caption 1 - Caption 2")}<p>Line 1</p>", editText.toHtml())
110112

111113
placeholderManager.removeItem {
112-
it.getValue("uuid") == uuid
114+
it.getValue("uuid") == uuid1
113115
}
114116

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

119121
@Test
120122
@Throws(Exception::class)
121-
fun insertOrUpdateAPlaceholderWhenInsertingBeforeNewLine() {
123+
fun doNotUpdatePlaceholderAtTheBeginningWhenMergeDisabled() {
124+
runBlocking {
125+
val initialHtml = "<p>Line 1</p>"
126+
editText.fromHtml(initialHtml)
127+
128+
editText.setSelection(0)
129+
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 1")
130+
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Caption 2", shouldMergePlaceholders = false)
131+
132+
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())
133+
134+
placeholderManager.removeItem {
135+
it.getValue("uuid") == uuid1
136+
}
137+
138+
Assert.assertEquals("<br><placeholder uuid=\"uuid2\" type=\"image_with_caption\" src=\"image.jpg\" caption=\"Caption 2\" /><p>Line 1</p>", editText.toHtml())
139+
}
140+
}
141+
142+
@Test
143+
@Throws(Exception::class)
144+
fun updatePlaceholderWhenInsertingBeforeNewLine() {
122145
runBlocking {
123146
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
124147
editText.fromHtml(initialHtml)
@@ -132,7 +155,21 @@ class PlaceholderTest {
132155

133156
@Test
134157
@Throws(Exception::class)
135-
fun insertOrUpdateAPlaceholderWhenInsertingRightBefore() {
158+
fun doNotUpdatePlaceholderWhenInsertingBeforeNewLineAndMergeDisabled() {
159+
runBlocking {
160+
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
161+
editText.fromHtml(initialHtml)
162+
163+
editText.setSelection(editText.editableText.indexOf("1"))
164+
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Second", shouldMergePlaceholders = false)
165+
166+
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())
167+
}
168+
}
169+
170+
@Test
171+
@Throws(Exception::class)
172+
fun updatePlaceholderWhenInsertingRightBefore() {
136173
runBlocking {
137174
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
138175
editText.fromHtml(initialHtml)
@@ -144,18 +181,32 @@ class PlaceholderTest {
144181
}
145182
}
146183

184+
@Test
185+
@Throws(Exception::class)
186+
fun doNotUpdatePlaceholderWhenInsertingRightBeforeAndMergeDisabled() {
187+
runBlocking {
188+
val initialHtml = "<p>Line 1</p>${placeholderWithCaption("First")}<p>Line 2</p>"
189+
editText.fromHtml(initialHtml)
190+
191+
editText.setSelection(editText.editableText.indexOf("1") + 1)
192+
ImageWithCaptionAdapter.insertImageWithCaption(placeholderManager, "image.jpg", "Second", shouldMergePlaceholders = false)
193+
194+
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())
195+
}
196+
}
197+
147198
private fun placeholderWithCaption(caption: String): String {
148-
return "<placeholder src=\"image.jpg\" caption=\"$caption\" uuid=\"uuid123\" type=\"image_with_caption\" />"
199+
return "<placeholder src=\"image.jpg\" caption=\"$caption\" uuid=\"uuid1\" type=\"image_with_caption\" />"
149200
}
150201

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

158-
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
209+
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
159210
true
160211
}) { currentAttributes ->
161212
val result = mutableMapOf<String, String>()
@@ -164,21 +215,21 @@ class PlaceholderTest {
164215
result
165216
}
166217

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

171222
@Test
172223
@Throws(Exception::class)
173224
fun updatePlaceholderAtTheEnd() {
174225
runBlocking {
175-
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>"
226+
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>"
176227
editText.fromHtml(initialHtml)
177228
editText.setSelection(editText.editableText.indexOf("First") + 1)
178229
val initialSelectionStart = editText.selectionStart
179230
val initialSelectionEnd = editText.selectionEnd
180231

181-
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
232+
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
182233
true
183234
}) { currentAttributes ->
184235
val result = mutableMapOf<String, String>()
@@ -187,7 +238,7 @@ class PlaceholderTest {
187238
result
188239
}
189240

190-
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())
241+
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())
191242
Assert.assertEquals(initialSelectionStart, editText.selectionStart)
192243
Assert.assertEquals(initialSelectionEnd, editText.selectionEnd)
193244
}
@@ -197,10 +248,10 @@ class PlaceholderTest {
197248
@Throws(Exception::class)
198249
fun removePlaceholderWhenItShouldNotBeUpdated() {
199250
runBlocking {
200-
val initialHtml = "<placeholder uuid=\"uuid123\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
251+
val initialHtml = "<placeholder uuid=\"uuid1\" type=\"image_with_caption\" src=\"image.jpg;image2.jpg\" caption=\"Caption - 1, 2\" /><p>Line</p>"
201252
editText.fromHtml(initialHtml)
202253

203-
placeholderManager.removeOrUpdate("uuid123", shouldUpdateItem = {
254+
placeholderManager.removeOrUpdate("uuid1", shouldUpdateItem = {
204255
false
205256
}) { currentAttributes ->
206257
val result = mutableMapOf<String, String>()

0 commit comments

Comments
 (0)