Skip to content

Commit 2ff653a

Browse files
authored
Merge pull request #988 from wordpress-mobile/fix/placeholder-position-bug-after-multiline-text
Stop using custom getLineForOffset
2 parents e8c934f + 4299db6 commit 2ff653a

File tree

4 files changed

+35
-38
lines changed

4 files changed

+35
-38
lines changed

media-placeholders/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependencies {
3737
implementation aztecProjectDependency
3838
implementation "com.github.bumptech.glide:glide:$glideVersion"
3939
implementation 'androidx.appcompat:appcompat:1.0.0'
40+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
4041
testImplementation "junit:junit:$jUnitVersion"
4142
}
4243

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.wordpress.aztec.placeholders
22

33
import android.content.Context
44
import android.graphics.drawable.Drawable
5+
import kotlinx.coroutines.runBlocking
56
import org.wordpress.aztec.AztecAttributes
67
import org.wordpress.aztec.AztecText
78
import org.wordpress.aztec.spans.AztecMediaSpan
@@ -23,6 +24,6 @@ class AztecPlaceholderSpan(
2324
}
2425

2526
override fun getMaxWidth(editorWidth: Int): Int {
26-
return adapter.calculateWidth(attributes, editorWidth)
27+
return runBlocking { adapter.calculateWidth(attributes, editorWidth) }
2728
}
2829
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ class ImageWithCaptionAdapter(
1818
override val type: String = "image_with_caption"
1919
) : PlaceholderManager.PlaceholderAdapter {
2020
private val media = mutableMapOf<String, ImageWithCaptionObject>()
21-
override fun getHeight(attrs: AztecAttributes): Proportion {
21+
suspend override fun getHeight(attrs: AztecAttributes): Proportion {
2222
return Proportion.Ratio(0.5f)
2323
}
24-
override fun createView(context: Context, placeholderUuid: String, attrs: AztecAttributes): View {
25-
val imageWithCaptionObject = media[placeholderUuid] ?: ImageWithCaptionObject(placeholderUuid, attrs.getValue(SRC_ATTRIBUTE), View.generateViewId()).apply {
26-
media[placeholderUuid] = this
27-
}
24+
25+
suspend override fun createView(context: Context, placeholderUuid: String, attrs: AztecAttributes): View {
26+
val imageWithCaptionObject = media[placeholderUuid]
27+
?: ImageWithCaptionObject(placeholderUuid, attrs.getValue(SRC_ATTRIBUTE), View.generateViewId()).apply {
28+
media[placeholderUuid] = this
29+
}
2830
val captionLayoutId = View.generateViewId()
2931
val imageLayoutId = imageWithCaptionObject.layoutId
3032
val linearLayout = LinearLayout(context)
@@ -61,7 +63,7 @@ class ImageWithCaptionAdapter(
6163
return linearLayout
6264
}
6365

64-
override fun onViewCreated(view: View, placeholderUuid: String) {
66+
suspend override fun onViewCreated(view: View, placeholderUuid: String) {
6567
val image = media[placeholderUuid]!!
6668
val imageView = view.findViewById<ImageView>(image.layoutId)
6769
Glide.with(view).load(image.src).into(imageView)
@@ -79,7 +81,7 @@ class ImageWithCaptionAdapter(
7981
private const val CAPTION_ATTRIBUTE = "caption"
8082
private const val SRC_ATTRIBUTE = "src"
8183

82-
fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String) {
84+
suspend fun insertImageWithCaption(placeholderManager: PlaceholderManager, src: String, caption: String) {
8385
placeholderManager.insertItem(ADAPTER_TYPE, SRC_ATTRIBUTE to src, CAPTION_ATTRIBUTE to caption)
8486
}
8587
}

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

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import android.view.View
1212
import android.view.ViewTreeObserver
1313
import android.widget.FrameLayout
1414
import androidx.core.content.ContextCompat
15+
import kotlinx.coroutines.CoroutineScope
16+
import kotlinx.coroutines.launch
17+
import kotlinx.coroutines.runBlocking
1518
import org.wordpress.aztec.AztecAttributes
1619
import org.wordpress.aztec.AztecContentChangeWatcher
1720
import org.wordpress.aztec.AztecText
@@ -33,6 +36,7 @@ import kotlin.math.min
3336
class PlaceholderManager(
3437
private val aztecText: AztecText,
3538
private val container: FrameLayout,
39+
private val coroutineScope: CoroutineScope,
3640
private val htmlTag: String = DEFAULT_HTML_TAG
3741
) : AztecContentChangeWatcher.AztecTextChangeObserver,
3842
IHtmlTagHandler,
@@ -67,7 +71,7 @@ class PlaceholderManager(
6771
* @param type placeholder type
6872
* @param attributes other attributes passed to the view. For example a `src` for an image.
6973
*/
70-
fun insertItem(type: String, vararg attributes: Pair<String, String>) {
74+
suspend fun insertItem(type: String, vararg attributes: Pair<String, String>) {
7175
val adapter = adapters[type]
7276
?: throw IllegalArgumentException("Adapter for inserted type not found. Register it with `registerAdapter` method")
7377
val attrs = getAttributesForMedia(type, attributes)
@@ -97,21 +101,21 @@ class PlaceholderManager(
97101
aztecText.refreshText(false)
98102
}
99103

100-
private fun buildPlaceholderDrawable(adapter: PlaceholderAdapter, attrs: AztecAttributes): Drawable {
104+
private suspend fun buildPlaceholderDrawable(adapter: PlaceholderAdapter, attrs: AztecAttributes): Drawable {
101105
val drawable = ContextCompat.getDrawable(aztecText.context, android.R.color.transparent)!!
102106
updateDrawableBounds(adapter, attrs, drawable)
103107
return drawable
104108
}
105109

106-
private fun updateAllBelowSelection(selectionStart: Int) {
110+
private suspend fun updateAllBelowSelection(selectionStart: Int) {
107111
positionToId.filter {
108112
it.elementPosition >= selectionStart - 1
109113
}.forEach {
110114
insertContentOverSpanWithId(it.uuid, it.elementPosition)
111115
}
112116
}
113117

114-
private fun insertContentOverSpanWithId(uuid: String, currentPosition: Int? = null) {
118+
private suspend fun insertContentOverSpanWithId(uuid: String, currentPosition: Int? = null) {
115119
var aztecAttributes: AztecAttributes? = null
116120
val predicate = object : AztecText.AttributePredicate {
117121
override fun matches(attrs: Attributes): Boolean {
@@ -127,15 +131,15 @@ class PlaceholderManager(
127131
insertInPosition(aztecAttributes ?: return, targetPosition, currentPosition)
128132
}
129133

130-
private fun insertInPosition(attrs: AztecAttributes, targetPosition: Int, currentPosition: Int? = null) {
134+
private suspend fun insertInPosition(attrs: AztecAttributes, targetPosition: Int, currentPosition: Int? = null) {
131135
if (!validateAttributes(attrs)) {
132136
return
133137
}
134138
val uuid = attrs.getValue(UUID_ATTRIBUTE)
135139
val type = attrs.getValue(TYPE_ATTRIBUTE)
136140
val textViewLayout: Layout = aztecText.layout
137141
val parentTextViewRect = Rect()
138-
val targetLineOffset = getLineForOffset(targetPosition)
142+
val targetLineOffset = textViewLayout.getLineForOffset(targetPosition)
139143
if (currentPosition != null) {
140144
if (targetLineOffset != 0 && currentPosition == targetPosition) {
141145
return
@@ -177,19 +181,6 @@ class PlaceholderManager(
177181
}
178182
}
179183

180-
private fun getLineForOffset(offset: Int): Int {
181-
var counter = 0
182-
var index = 0
183-
for (line in aztecText.text.split("\n")) {
184-
counter += line.length + 1
185-
if (counter > offset) {
186-
break
187-
}
188-
index += 1
189-
}
190-
return index
191-
}
192-
193184
private fun validateAttributes(attributes: AztecAttributes): Boolean {
194185
return attributes.hasAttribute(UUID_ATTRIBUTE) &&
195186
attributes.hasAttribute(TYPE_ATTRIBUTE) &&
@@ -210,7 +201,9 @@ class PlaceholderManager(
210201
* Called when the aztec text content changes.
211202
*/
212203
override fun onContentChanged() {
213-
updateAllBelowSelection(aztecText.selectionStart)
204+
coroutineScope.launch {
205+
updateAllBelowSelection(aztecText.selectionStart)
206+
}
214207
}
215208

216209
/**
@@ -255,7 +248,7 @@ class PlaceholderManager(
255248
val adapter = adapters[type] ?: return false
256249
val aztecAttributes = AztecAttributes(attributes)
257250
aztecAttributes.setValue(UUID_ATTRIBUTE, UUID.randomUUID().toString())
258-
val drawable = buildPlaceholderDrawable(adapter, aztecAttributes)
251+
val drawable = runBlocking { buildPlaceholderDrawable(adapter, aztecAttributes) }
259252
val span = AztecPlaceholderSpan(
260253
context = aztecText.context,
261254
drawable = drawable,
@@ -291,8 +284,8 @@ class PlaceholderManager(
291284
spans.forEach {
292285
val type = it.attributes.getValue(TYPE_ATTRIBUTE)
293286
val adapter = adapters[type] ?: return
294-
updateDrawableBounds(adapter, it.attributes, it.drawable)
295-
aztecText.post {
287+
coroutineScope.launch {
288+
updateDrawableBounds(adapter, it.attributes, it.drawable)
296289
aztecText.refreshText(false)
297290
insertInPosition(it.attributes, aztecText.editableText.getSpanStart(it))
298291
}
@@ -301,7 +294,7 @@ class PlaceholderManager(
301294
})
302295
}
303296

304-
private fun updateDrawableBounds(adapter: PlaceholderAdapter, attrs: AztecAttributes, drawable: Drawable?) {
297+
private suspend fun updateDrawableBounds(adapter: PlaceholderAdapter, attrs: AztecAttributes, drawable: Drawable?) {
305298
val editorWidth = if (aztecText.width > 0) aztecText.width else aztecText.maxImagesWidth
306299
if (drawable?.bounds?.right != editorWidth) {
307300
drawable?.setBounds(0, 0, adapter.calculateWidth(attrs, editorWidth), adapter.calculateHeight(attrs, editorWidth))
@@ -335,15 +328,15 @@ class PlaceholderManager(
335328
* @param placeholderUuid the placeholder UUID
336329
* @param attrs aztec attributes of the view
337330
*/
338-
fun createView(context: Context, placeholderUuid: String, attrs: AztecAttributes): View
331+
suspend fun createView(context: Context, placeholderUuid: String, attrs: AztecAttributes): View
339332

340333
/**
341334
* Called after the view is measured. Use this method if you need the actual width and height of the view to
342335
* draw your media.
343336
* @param view the frame layout wrapping the custom view
344337
* @param placeholderUuid the placeholder ID
345338
*/
346-
fun onViewCreated(view: View, placeholderUuid: String) {}
339+
suspend fun onViewCreated(view: View, placeholderUuid: String) {}
347340

348341
/**
349342
* Called when the placeholder is deleted by the user. Use this method if you need to clear your data when the
@@ -374,18 +367,18 @@ class PlaceholderManager(
374367
* Returns width of the view based on the HTML attributes. Use this method to either set fixed width or to
375368
* calculate width based on the view.
376369
*/
377-
fun getWidth(attrs: AztecAttributes): Proportion = Proportion.Ratio(1.0f)
370+
suspend fun getWidth(attrs: AztecAttributes): Proportion = Proportion.Ratio(1.0f)
378371

379372
/**
380373
* Returns height of the view based on the HTML attributes. Use this method to either set fixed height or to
381374
* calculate width based on the view.
382375
*/
383-
fun getHeight(attrs: AztecAttributes): Proportion
376+
suspend fun getHeight(attrs: AztecAttributes): Proportion
384377

385378
/**
386379
* Returns height of the view based on the width and the placeholder height.
387380
*/
388-
fun calculateHeight(attrs: AztecAttributes, windowWidth: Int): Int {
381+
suspend fun calculateHeight(attrs: AztecAttributes, windowWidth: Int): Int {
389382
return getHeight(attrs).let { height ->
390383
when (height) {
391384
is Proportion.Fixed -> height.value
@@ -404,7 +397,7 @@ class PlaceholderManager(
404397
/**
405398
* Returns height of the view based on the width and the placeholder height.
406399
*/
407-
fun calculateWidth(attrs: AztecAttributes, windowWidth: Int): Int {
400+
suspend fun calculateWidth(attrs: AztecAttributes, windowWidth: Int): Int {
408401
return getWidth(attrs).let { width ->
409402
when (width) {
410403
is Proportion.Fixed -> min(windowWidth, width.value)

0 commit comments

Comments
 (0)