From 929eaf48dc4378f144c2234326ea01a4230d06eb Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 26 Jul 2019 08:16:02 -0300 Subject: [PATCH 1/4] avoid calling CssStyleFormatter.mergeStyleAttributes if parentStyle or childStyle are null --- .../kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt index c0b35efa0..426dd93c7 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/plugins/CssUnderlinePlugin.kt @@ -41,7 +41,9 @@ class CssUnderlinePlugin : ISpanPostprocessor, ISpanPreprocessor { if (hiddenSpan.TAG == SPAN_TAG) { val parentStyle = hiddenSpan.attributes.getValue(CssStyleFormatter.STYLE_ATTRIBUTE) val childStyle = calypsoUnderlineSpan.attributes.getValue(CssStyleFormatter.STYLE_ATTRIBUTE) - hiddenSpan.attributes.setValue(CssStyleFormatter.STYLE_ATTRIBUTE, CssStyleFormatter.mergeStyleAttributes(parentStyle, childStyle)) + if (parentStyle != null && childStyle != null) { + hiddenSpan.attributes.setValue(CssStyleFormatter.STYLE_ATTRIBUTE, CssStyleFormatter.mergeStyleAttributes(parentStyle, childStyle)) + } // remove the extra child span spannable.removeSpan(calypsoUnderlineSpan) From 0ca13ce65ebe42f3e42deb16d50dac0839072df9 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 26 Jul 2019 16:54:08 -0300 Subject: [PATCH 2/4] added code to keep instance of InputConnection as long as parameters passed remain the same per each call --- .../kotlin/org/wordpress/aztec/AztecText.kt | 36 ++++++++++++ .../wordpress/aztec/ime/EditorInfoUtils.kt | 55 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 aztec/src/main/kotlin/org/wordpress/aztec/ime/EditorInfoUtils.kt diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index a6b2beeb7..cea1f0021 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -48,6 +48,8 @@ import android.view.MotionEvent import android.view.View import android.view.WindowManager import android.view.inputmethod.BaseInputConnection +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputConnection import android.widget.CheckBox import android.widget.EditText import android.widget.Toast @@ -70,6 +72,7 @@ import org.wordpress.aztec.handlers.ListHandler import org.wordpress.aztec.handlers.ListItemHandler import org.wordpress.aztec.handlers.PreformatHandler import org.wordpress.aztec.handlers.QuoteHandler +import org.wordpress.aztec.ime.EditorInfoUtils import org.wordpress.aztec.plugins.IAztecPlugin import org.wordpress.aztec.plugins.IToolbarButton import org.wordpress.aztec.source.Format @@ -291,6 +294,9 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown private var focusOnVisible = true + var inputConnection: InputConnection? = null + var inputConnectionEditorInfo: EditorInfo? = null + interface OnSelectionChangedListener { fun onSelectionChanged(selStart: Int, selEnd: Int) } @@ -660,6 +666,36 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } } + override fun onCreateInputConnection(outAttrs: EditorInfo) : InputConnection { + // initialize inputConnectionEditorInfo + if (inputConnectionEditorInfo == null) { + inputConnectionEditorInfo = outAttrs + } + + // now init the InputConnection, or replace if EditorInfo contains anything different + if (inputConnection == null || !EditorInfoUtils.areEditorInfosTheSame(outAttrs, inputConnectionEditorInfo!!)) { + // we have a new InputConnection to create, save the new EditorInfo data and create it + // we make a copy of the parameters being received, because super.onCreateInputConnection may make changes + // to EditorInfo params being sent to it, and we want to preserve the same data we received in order + // to compare. + // (see https://android.googlesource.com/platform/frameworks/base/+/jb-mr0-release/core/java/android/widget/ + // TextView.java#5404) + inputConnectionEditorInfo = EditorInfoUtils.copyEditorInfo(outAttrs) + val localInputConnection = super.onCreateInputConnection(outAttrs) + if (localInputConnection == null) { + // in case super returns null, let's just observe the base implementation, no need to make + // an InputConnectionWrapper of a null target + return localInputConnection + } + // if non null, wrap the new InputConnection around our wrapper + //inputConnection = AztecTextInputConnectionWrapper(localInputConnection, this) + inputConnection = localInputConnection + } + + // returnn the existing inputConnection + return inputConnection!! + } + override fun onRestoreInstanceState(state: Parcelable?) { disableTextChangedListener() diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/ime/EditorInfoUtils.kt b/aztec/src/main/kotlin/org/wordpress/aztec/ime/EditorInfoUtils.kt new file mode 100644 index 000000000..abb591fe0 --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/ime/EditorInfoUtils.kt @@ -0,0 +1,55 @@ +package org.wordpress.aztec.ime + +import android.os.Build +import android.view.inputmethod.EditorInfo +import java.util.Arrays + +object EditorInfoUtils { + @JvmStatic + fun areEditorInfosTheSame(ed1: EditorInfo, ed2: EditorInfo): Boolean { + if (ed1 == ed2) { + return true + } + + if (ed1.actionId == ed2.actionId + && (ed1.actionLabel != null && ed1.actionLabel.equals(ed2.actionLabel) || ed1.actionLabel == null && ed2.actionLabel == null) + && ed1.inputType == ed2.inputType + && ed1.imeOptions == ed2.imeOptions + && (ed1.privateImeOptions != null && ed1.privateImeOptions.equals(ed2.privateImeOptions) || ed1.privateImeOptions == null && ed2.privateImeOptions == null) + && ed1.initialSelStart == ed2.initialSelStart + && ed1.initialSelEnd == ed2.initialSelEnd + && ed1.initialCapsMode == ed2.initialCapsMode + && ed1.fieldId == ed2.fieldId + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + // specific comparisons here + if (ed1.contentMimeTypes != null && ed2.contentMimeTypes != null) { + return Arrays.equals(ed1.contentMimeTypes, ed2.contentMimeTypes) + } + } + return true + } + return false + } + + @JvmStatic + fun copyEditorInfo(ed1: EditorInfo) : EditorInfo { + val copy = EditorInfo() + copy.actionId = ed1.actionId + copy.actionLabel = ed1.actionLabel?.toString() + copy.inputType = ed1.inputType + copy.imeOptions = ed1.imeOptions + copy.privateImeOptions = ed1.privateImeOptions?.toString() + copy.initialSelStart = ed1.initialSelStart + copy.initialSelEnd = ed1.initialSelEnd + copy.initialCapsMode = ed1.initialCapsMode + copy.fieldId = ed1.fieldId + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + // specific comparisons here + if (ed1.contentMimeTypes != null) { + copy.contentMimeTypes = Arrays.copyOf(ed1.contentMimeTypes, ed1.contentMimeTypes.size) + } + } + return copy + } +} \ No newline at end of file From d20fb8572a8f29886d51dbf4335c46f33944e10a Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 12 Aug 2019 13:05:45 -0300 Subject: [PATCH 3/4] using WeakReference in InputConnection --- .../src/main/kotlin/org/wordpress/aztec/AztecText.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index cea1f0021..7a000c309 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -121,6 +121,7 @@ import org.wordpress.aztec.watchers.event.text.BeforeTextChangedEventData import org.wordpress.aztec.watchers.event.text.OnTextChangedEventData import org.wordpress.aztec.watchers.event.text.TextWatcherEvent import org.xml.sax.Attributes +import java.lang.ref.WeakReference import java.security.MessageDigest import java.security.NoSuchAlgorithmException import java.util.ArrayList @@ -294,7 +295,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown private var focusOnVisible = true - var inputConnection: InputConnection? = null + var inputConnectionRef: WeakReference? = null var inputConnectionEditorInfo: EditorInfo? = null interface OnSelectionChangedListener { @@ -673,7 +674,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } // now init the InputConnection, or replace if EditorInfo contains anything different - if (inputConnection == null || !EditorInfoUtils.areEditorInfosTheSame(outAttrs, inputConnectionEditorInfo!!)) { + if (inputConnectionRef?.get() == null || !EditorInfoUtils.areEditorInfosTheSame(outAttrs, inputConnectionEditorInfo!!)) { // we have a new InputConnection to create, save the new EditorInfo data and create it // we make a copy of the parameters being received, because super.onCreateInputConnection may make changes // to EditorInfo params being sent to it, and we want to preserve the same data we received in order @@ -689,11 +690,11 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } // if non null, wrap the new InputConnection around our wrapper //inputConnection = AztecTextInputConnectionWrapper(localInputConnection, this) - inputConnection = localInputConnection + inputConnectionRef = WeakReference(localInputConnection) } - // returnn the existing inputConnection - return inputConnection!! + // return the existing inputConnection + return inputConnectionRef?.get()!! } override fun onRestoreInstanceState(state: Parcelable?) { From 2169affc15e7804e17259ea119abe0409db6100f Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Mon, 14 Oct 2019 11:38:09 -0300 Subject: [PATCH 4/4] conditionally applying patch reusing InputConnection on Android Oreo for now --- .../src/main/kotlin/org/wordpress/aztec/AztecText.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 7a000c309..7c61c2d03 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -668,6 +668,15 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } override fun onCreateInputConnection(outAttrs: EditorInfo) : InputConnection { + // limiting the reuseInputConnection fix for Anroid 8.0.0 for now + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { + return handleReuseInputConnection(outAttrs) + } + + return super.onCreateInputConnection(outAttrs) + } + + private fun handleReuseInputConnection(outAttrs: EditorInfo) : InputConnection { // initialize inputConnectionEditorInfo if (inputConnectionEditorInfo == null) { inputConnectionEditorInfo = outAttrs @@ -688,7 +697,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown // an InputConnectionWrapper of a null target return localInputConnection } - // if non null, wrap the new InputConnection around our wrapper + // if non null, wrap the new InputConnection around our wrapper (used for logging purposes only) //inputConnection = AztecTextInputConnectionWrapper(localInputConnection, this) inputConnectionRef = WeakReference(localInputConnection) }