Skip to content

Commit 2d66175

Browse files
authored
Merge pull request #1088 from wordpress-mobile/issue/fix-media-span-and-task-list-memory-leaks
Fix media span and task list memory leaks
2 parents db247eb + 1d07842 commit 2d66175

File tree

3 files changed

+33
-17
lines changed

3 files changed

+33
-17
lines changed

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

+15-7
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
955955
blockEditorDialog!!.dismiss()
956956
}
957957
EnhancedMovementMethod.setLinkTappedListener(null)
958+
clearTaskListRefreshListeners()
958959
}
959960

960961
// We are exposing this method in order to allow subclasses to set their own alpha value
@@ -1711,19 +1712,19 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
17111712
val imageSpans = editable.getSpans(start, end, AztecImageSpan::class.java)
17121713
imageSpans.forEach {
17131714
it.onImageTappedListener = onImageTappedListener
1714-
it.onMediaDeletedListener = onMediaDeletedListener
1715+
it.setOnMediaDeletedListener(onMediaDeletedListener)
17151716
}
17161717

17171718
val videoSpans = editable.getSpans(start, end, AztecVideoSpan::class.java)
17181719
videoSpans.forEach {
17191720
it.onVideoTappedListener = onVideoTappedListener
1720-
it.onMediaDeletedListener = onMediaDeletedListener
1721+
it.setOnMediaDeletedListener(onMediaDeletedListener)
17211722
}
17221723

17231724
val audioSpans = editable.getSpans(start, end, AztecAudioSpan::class.java)
17241725
audioSpans.forEach {
17251726
it.onAudioTappedListener = onAudioTappedListener
1726-
it.onMediaDeletedListener = onMediaDeletedListener
1727+
it.setOnMediaDeletedListener(onMediaDeletedListener)
17271728
}
17281729

17291730
val unknownHtmlSpans = editable.getSpans(start, end, UnknownHtmlSpan::class.java)
@@ -1763,9 +1764,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
17631764
taskList.onRefresh = null
17641765
this.editableText.removeSpan(taskList)
17651766
val newSpan = if (taskList is AztecTaskListSpanAligned) {
1766-
AztecTaskListSpanAligned(taskList.nestingLevel, taskList.attributes, taskList.context, taskList.listStyle, taskList.align)
1767+
AztecTaskListSpanAligned(taskList.nestingLevel, taskList.attributes, context, taskList.listStyle, taskList.align)
17671768
} else {
1768-
AztecTaskListSpan(taskList.nestingLevel, taskList.attributes, taskList.context, taskList.listStyle)
1769+
AztecTaskListSpan(taskList.nestingLevel, taskList.attributes, context, taskList.listStyle)
17691770
}
17701771
newSpan.onRefresh = {
17711772
refreshTaskListSpan(it)
@@ -1774,6 +1775,13 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
17741775
setSelection(selStart, selEnd)
17751776
}
17761777

1778+
private fun clearTaskListRefreshListeners() {
1779+
val taskLists = this.editableText.getSpans(0, this.editableText.length, AztecTaskListSpan::class.java)
1780+
taskLists.forEach { taskList ->
1781+
taskList.onRefresh = null
1782+
}
1783+
}
1784+
17771785
fun disableTextChangedListener() {
17781786
consumeEditEvent = true
17791787
}
@@ -2217,7 +2225,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
22172225
* Use this method to insert a custom AztecMediaSpan at the cursor position
22182226
*/
22192227
fun insertMediaSpan(span: AztecMediaSpan) {
2220-
span.onMediaDeletedListener = onMediaDeletedListener
2228+
span.setOnMediaDeletedListener(onMediaDeletedListener)
22212229
lineBlockFormatter.insertMediaSpan(shouldAddMediaInline, span)
22222230
}
22232231

@@ -2323,7 +2331,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
23232331

23242332
text.removeSpan(clickableSpan)
23252333
text.removeSpan(mediaSpan)
2326-
aztecMediaSpan.onMediaDeletedListener = onMediaDeletedListener
2334+
aztecMediaSpan.setOnMediaDeletedListener(onMediaDeletedListener)
23272335
lineBlockFormatter.insertMediaSpanOverCurrentChar(aztecMediaSpan, start)
23282336
contentChangeWatcher.notifyContentChanged()
23292337
}

aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ import java.lang.ref.WeakReference
1111
import java.util.ArrayList
1212

1313
abstract class AztecMediaSpan(drawable: Drawable?, override var attributes: AztecAttributes = AztecAttributes(),
14-
var onMediaDeletedListener: AztecText.OnMediaDeletedListener? = null,
14+
onMediaDeletedListener: AztecText.OnMediaDeletedListener? = null,
1515
editor: AztecText? = null) : AztecDynamicImageSpan(drawable), IAztecAttributedSpan {
1616
abstract val TAG: String
1717

1818
private val overlays: ArrayList<Pair<Drawable?, Int>> = ArrayList()
1919

20+
private var onMediaDeletedListenerRef = WeakReference(onMediaDeletedListener)
21+
22+
fun setOnMediaDeletedListener(listener: AztecText.OnMediaDeletedListener?) {
23+
onMediaDeletedListenerRef = WeakReference(listener)
24+
}
25+
2026
init {
2127
textView = editor?.let { WeakReference(editor) }
2228
}
@@ -96,9 +102,9 @@ abstract class AztecMediaSpan(drawable: Drawable?, override var attributes: Azte
96102
abstract fun onClick()
97103

98104
fun onMediaDeleted() {
99-
onMediaDeletedListener?.onMediaDeleted(attributes)
105+
onMediaDeletedListenerRef.get()?.onMediaDeleted(attributes)
100106
}
101107
fun beforeMediaDeleted() {
102-
onMediaDeletedListener?.beforeMediaDeleted(attributes)
108+
onMediaDeletedListenerRef.get()?.beforeMediaDeleted(attributes)
103109
}
104110
}

aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecTaskListSpan.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.wordpress.aztec.ITextFormat
3030
import org.wordpress.aztec.R
3131
import org.wordpress.aztec.formatting.BlockFormatter
3232
import org.wordpress.aztec.setTaskList
33+
import java.lang.ref.WeakReference
3334

3435
fun createTaskListSpan(
3536
nestingLevel: Int,
@@ -61,11 +62,12 @@ class AztecTaskListSpanAligned(
6162
open class AztecTaskListSpan(
6263
override var nestingLevel: Int,
6364
override var attributes: AztecAttributes = AztecAttributes(),
64-
val context: Context,
65+
context: Context,
6566
var listStyle: BlockFormatter.ListStyle = BlockFormatter.ListStyle(0, 0, 0, 0, 0),
6667
var onRefresh: ((AztecTaskListSpan) -> Unit)? = null
6768
) : AztecListSpan(nestingLevel, listStyle.verticalPadding) {
6869
private var toggled: Boolean = false
70+
private var contextRef: WeakReference<Context> = WeakReference(context)
6971
override val TAG = "ul"
7072

7173
override val startTag: String
@@ -82,7 +84,7 @@ open class AztecTaskListSpan(
8284
top: Int, baseline: Int, bottom: Int,
8385
text: CharSequence, start: Int, end: Int,
8486
first: Boolean, l: Layout) {
85-
if (!first) return
87+
if (!first || contextRef.get() == null) return
8688

8789
val spanStart = (text as Spanned).getSpanStart(this)
8890
val spanEnd = text.getSpanEnd(this)
@@ -99,24 +101,24 @@ open class AztecTaskListSpan(
99101
val drawableHeight = (0.8 * (p.fontMetrics.bottom - p.fontMetrics.top))
100102
// Make sure the marker is correctly aligned on RTL languages
101103
val markerStartPosition: Float = x + (listStyle.indicatorMargin * dir) * 1f
102-
val d: Drawable = context.resources.getDrawable(R.drawable.ic_checkbox, null)
104+
val d: Drawable? = contextRef.get()?.resources?.getDrawable(R.drawable.ic_checkbox, null)
103105
val leftBound = markerStartPosition.toInt()
104106
if (isChecked(text, lineIndex)) {
105-
d.state = intArrayOf(android.R.attr.state_checked)
107+
d?.state = intArrayOf(android.R.attr.state_checked)
106108
} else {
107-
d.state = intArrayOf()
109+
d?.state = intArrayOf()
108110
}
109111
val (startShift, endShift) = if (dir > 0) {
110112
0.8 to 0.2
111113
} else {
112114
0.2 to 0.8
113115
}
114116

115-
d.setBounds((leftBound - drawableHeight * startShift).toInt().coerceAtLeast(0),
117+
d?.setBounds((leftBound - drawableHeight * startShift).toInt().coerceAtLeast(0),
116118
(baseline - drawableHeight * 0.8).toInt(),
117119
(leftBound + drawableHeight * endShift).toInt(),
118120
(baseline + drawableHeight * 0.2).toInt())
119-
d.draw(c)
121+
d?.draw(c)
120122

121123
p.color = oldColor
122124
p.style = style

0 commit comments

Comments
 (0)