Skip to content

Commit 40cde2f

Browse files
authored
Merge pull request #804 from wordpress-mobile/release/1.3.24
Merge Release/1.3.24 into master
2 parents 1b6e4b9 + b79d028 commit 40cde2f

File tree

8 files changed

+135
-8
lines changed

8 files changed

+135
-8
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
# Changelog
2+
## [v1.3.24](https://github.com/wordpress-mobile/AztecEditor-Android/releases/tag/v1.3.24)
3+
### Changed
4+
- Improvement to how clickable URL spans are handled #793
5+
6+
## [v1.3.23](https://github.com/wordpress-mobile/AztecEditor-Android/releases/tag/v1.3.23)
7+
### Fixed
8+
- Fix for crash when inserting characters right before an image #801
9+
210
## [v1.3.22](https://github.com/wordpress-mobile/AztecEditor-Android/releases/tag/v1.3.22)
311
### Fixed
412
- Fix for crash on Samsung Clipboard ListView on Android 8.X #795

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ repositories {
105105
```
106106
```gradle
107107
dependencies {
108-
api ('com.github.wordpress-mobile.WordPress-Aztec-Android:aztec:v1.3.22')
108+
api ('com.github.wordpress-mobile.WordPress-Aztec-Android:aztec:v1.3.24')
109109
}
110110
```
111111

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ open class Aztec private constructor(val visualEditor: AztecText, val toolbar: I
2424
private var onAudioTappedListener: AztecText.OnAudioTappedListener? = null
2525
private var onMediaDeletedListener: AztecText.OnMediaDeletedListener? = null
2626
private var onVideoInfoRequestedListener: AztecText.OnVideoInfoRequestedListener? = null
27+
private var onLinkTappedListener: AztecText.OnLinkTappedListener? = null
28+
private var isLinkTapEnabled: Boolean = false
2729
private var plugins: ArrayList<IAztecPlugin> = visualEditor.plugins
2830
var sourceEditor: SourceViewEditText? = null
2931

@@ -134,6 +136,18 @@ open class Aztec private constructor(val visualEditor: AztecText, val toolbar: I
134136
return this
135137
}
136138

139+
fun setOnLinkTappedListener(onLinkTappedListener: AztecText.OnLinkTappedListener): Aztec {
140+
this.onLinkTappedListener = onLinkTappedListener
141+
initLinkTappedListener()
142+
return this
143+
}
144+
145+
fun setLinkTapEnabled(isLinkTapEnabled: Boolean): Aztec {
146+
this.isLinkTapEnabled = isLinkTapEnabled
147+
initLinkTapEnabled()
148+
return this
149+
}
150+
137151
fun addPlugin(plugin: IAztecPlugin): Aztec {
138152
plugins.add(plugin)
139153

@@ -219,4 +233,14 @@ open class Aztec private constructor(val visualEditor: AztecText, val toolbar: I
219233
visualEditor.setOnVideoInfoRequestedListener(onVideoInfoRequestedListener!!)
220234
}
221235
}
236+
237+
private fun initLinkTappedListener() {
238+
if (onLinkTappedListener != null) {
239+
visualEditor.setOnLinkTappedListener(onLinkTappedListener!!)
240+
}
241+
}
242+
243+
private fun initLinkTapEnabled() {
244+
visualEditor.setLinkTapEnabled(isLinkTapEnabled)
245+
}
222246
}

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

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
224224
private var consumeSelectionChangedEvent: Boolean = false
225225
private var isInlineTextHandlerEnabled: Boolean = true
226226
private var bypassObservationQueue: Boolean = false
227+
private var bypassMediaDeletedListener: Boolean = false
228+
private var bypassCrashPreventerInputFilter: Boolean = false
227229

228230
var initialEditorContentParsedSHA256: ByteArray = ByteArray(0)
229231

@@ -322,6 +324,10 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
322324
fun onBackspaceKey() : Boolean
323325
}
324326

327+
interface OnLinkTappedListener {
328+
fun onLinkTapped(widget: View, url: String)
329+
}
330+
325331
constructor(context: Context) : super(context) {
326332
init(null)
327333
}
@@ -420,7 +426,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
420426
// triggers ClickableSpan onClick() events
421427
movementMethod = EnhancedMovementMethod
422428

423-
setupBackspaceAndEnterDetection()
429+
setupKeyListenersAndInputFilters()
424430

425431
//disable auto suggestions/correct for older devices
426432
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
@@ -440,12 +446,52 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
440446
// Setup the keyListener(s) for Backspace and Enter key.
441447
// Backspace: If listener does return false we remove the style here
442448
// Enter: Ask the listener if we need to insert or not the char
443-
private fun setupBackspaceAndEnterDetection() {
449+
private fun setupKeyListenersAndInputFilters() {
444450
//hardware keyboard
445451
setOnKeyListener { _, _, event ->
446452
handleBackspaceAndEnter(event)
447453
}
448454

455+
// This InputFilter created only for the purpose of avoiding crash described here:
456+
// https://android-review.googlesource.com/c/platform/frameworks/base/+/634929
457+
// https://github.com/wordpress-mobile/AztecEditor-Android/issues/729
458+
// the rationale behind this workaround is that the specific crash happens only when adding/deleting text right
459+
// before an AztecImageSpan, so we detect the specific case and re-create the contents only when that happens.
460+
// This is indeed tackling the symptom rather than the actual problem, and should be removed once the real
461+
// problem is fixed at the Android OS level as described in the following url
462+
// https://android-review.googlesource.com/c/platform/frameworks/base/+/634929
463+
val dynamicLayoutCrashPreventer = InputFilter { source, start, end, dest, dstart, dend ->
464+
var temp : CharSequence? = null
465+
if (!bypassCrashPreventerInputFilter && dstart == dend && dest.length > dend+1) {
466+
// dstart == dend means this is an insertion
467+
// if there are any images right after the destination position, hack the text
468+
val spans = dest.getSpans(dstart, dend+1, AztecImageSpan::class.java)
469+
if (spans.isNotEmpty()) {
470+
// prevent this filter from running twice when `text.insert()` gets called a few lines below
471+
disableCrashPreventerInputFilter()
472+
// disable MediaDeleted listener before operating on content
473+
disableMediaDeletedListener()
474+
475+
// take the source (that is, what is being inserted), and append the Image to it. We will delete
476+
// the original Image later so to not have a duplicate.
477+
// use Spannable to copy / keep the current spans
478+
temp = SpannableStringBuilder(source).append(dest.subSequence(dend, dend+1))
479+
480+
// delete the original AztecImageSpan
481+
text.delete(dend, dend+1)
482+
// now insert both the new insertion _and_ the original AztecImageSpan
483+
text.insert(dend, temp)
484+
temp = "" // discard the original source parameter as an ouput from this InputFilter
485+
486+
// re-enable MediaDeleted listener
487+
enableMediaDeletedListener()
488+
// re-enable this very filter
489+
enableCrashPreventerInputFilter()
490+
}
491+
}
492+
temp
493+
}
494+
449495
val emptyEditTextBackspaceDetector = InputFilter { source, start, end, dest, dstart, dend ->
450496
if (selectionStart == 0 && selectionEnd == 0
451497
&& end == 0 && start == 0
@@ -462,7 +508,12 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
462508
source
463509
}
464510

465-
filters = arrayOf(emptyEditTextBackspaceDetector)
511+
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O || Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
512+
// dynamicLayoutCrashPreventer needs to be first in array as these are going to be chained when processed
513+
filters = arrayOf(dynamicLayoutCrashPreventer, emptyEditTextBackspaceDetector)
514+
} else {
515+
filters = arrayOf(emptyEditTextBackspaceDetector)
516+
}
466517
}
467518

468519
private fun handleBackspaceAndEnter(event: KeyEvent): Boolean {
@@ -786,6 +837,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
786837
this.onVideoInfoRequestedListener = listener
787838
}
788839

840+
fun setOnLinkTappedListener(listener: OnLinkTappedListener) {
841+
EnhancedMovementMethod.linkTappedListener = listener
842+
}
843+
844+
fun setLinkTapEnabled(isLinkTapEnabled: Boolean) {
845+
EnhancedMovementMethod.isLinkTapEnabled = isLinkTapEnabled
846+
}
847+
789848
override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
790849
if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
791850
onImeBackListener?.onImeBack()
@@ -1303,6 +1362,26 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
13031362
bypassObservationQueue = false
13041363
}
13051364

1365+
fun disableCrashPreventerInputFilter() {
1366+
bypassCrashPreventerInputFilter = true
1367+
}
1368+
1369+
fun enableCrashPreventerInputFilter() {
1370+
bypassCrashPreventerInputFilter = false
1371+
}
1372+
1373+
fun disableMediaDeletedListener() {
1374+
bypassMediaDeletedListener = true
1375+
}
1376+
1377+
fun enableMediaDeletedListener() {
1378+
bypassMediaDeletedListener = false
1379+
}
1380+
1381+
fun isMediaDeletedListenerDisabled(): Boolean {
1382+
return bypassMediaDeletedListener
1383+
}
1384+
13061385
fun isTextChangedListenerDisabled(): Boolean {
13071386
return consumeEditEvent
13081387
}
@@ -1778,4 +1857,5 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
17781857
override fun dispatchHoverEvent(event: MotionEvent): Boolean {
17791858
return if (accessibilityDelegate.onHoverEvent(event)) true else super.dispatchHoverEvent(event)
17801859
}
1860+
17811861
}

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import android.text.style.ClickableSpan
77
import android.view.MotionEvent
88
import android.widget.TextView
99
import org.wordpress.aztec.spans.AztecMediaClickableSpan
10+
import org.wordpress.aztec.spans.AztecURLSpan
1011
import org.wordpress.aztec.spans.UnknownClickableSpan
1112

1213
/**
1314
* http://stackoverflow.com/a/23566268/569430
1415
*/
1516
object EnhancedMovementMethod : ArrowKeyMovementMethod() {
17+
var isLinkTapEnabled = false
18+
var linkTappedListener: AztecText.OnLinkTappedListener? = null
19+
1620
override fun onTouchEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean {
1721
val action = event.action
1822

@@ -72,11 +76,14 @@ object EnhancedMovementMethod : ArrowKeyMovementMethod() {
7276
link = text.getSpans(off, off, ClickableSpan::class.java).firstOrNull { text.getSpanStart(it) == off }
7377
}
7478

75-
if (link != null && (link is AztecMediaClickableSpan || link is UnknownClickableSpan)) {
76-
if (action == MotionEvent.ACTION_UP) {
79+
if (link != null) {
80+
if (link is AztecMediaClickableSpan || link is UnknownClickableSpan) {
7781
link.onClick(widget)
82+
return true
83+
} else if (link is AztecURLSpan && isLinkTapEnabled) {
84+
linkTappedListener?.onLinkTapped(widget, link.url) ?: link.onClick(widget)
85+
return true
7886
}
79-
return true
8087
}
8188
}
8289

aztec/src/main/kotlin/org/wordpress/aztec/watchers/DeleteMediaElementWatcherAPI25AndHigher.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class DeleteMediaElementWatcherAPI25AndHigher(aztecText: AztecText) : TextWatche
1717
return
1818
}
1919

20+
if (aztecTextRef.get()?.isMediaDeletedListenerDisabled() ?: true) {
21+
return
22+
}
23+
2024
if (count > 0) {
2125
deleted = true
2226

aztec/src/main/kotlin/org/wordpress/aztec/watchers/DeleteMediaElementWatcherPreAPI25.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class DeleteMediaElementWatcherPreAPI25(aztecText: AztecText) : TextWatcher {
1414
return
1515
}
1616

17+
if (aztecTextRef.get()?.isMediaDeletedListenerDisabled() ?: true) {
18+
return
19+
}
20+
1721
if (count > 0) {
1822
aztecTextRef.get()?.text?.getSpans(start, start + count, AztecMediaSpan::class.java)
1923
?.forEach {

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ buildscript {
99
picassoVersion = '2.5.2'
1010
robolectricVersion = '3.5.1'
1111
jUnitVersion = '4.12'
12-
jSoupVersion = '1.10.3'
12+
jSoupVersion = '1.11.3'
1313
wordpressUtilsVersion = '1.21'
1414
espressoVersion = '3.0.1'
1515
}

0 commit comments

Comments
 (0)