diff --git a/aztec/build.gradle b/aztec/build.gradle index fbacac837..ae7c97f3a 100644 --- a/aztec/build.gradle +++ b/aztec/build.gradle @@ -72,6 +72,8 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion" implementation 'org.apache.commons:commons-lang3:3.8.1' + + lintChecks "org.wordpress:lint:$wordpressLintVersion" } project.afterEvaluate { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index d45b4197f..346716122 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -36,6 +36,7 @@ import android.os.Parcelable import android.provider.Settings import android.text.Editable import android.text.InputFilter +import android.text.InputType import android.text.Spannable import android.text.SpannableStringBuilder import android.text.Spanned @@ -695,13 +696,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown } override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection { - val baseInputConnection = requireNotNull(super.onCreateInputConnection(outAttrs)).wrapWithBackSpaceHandler() - return if (shouldOverridePredictiveTextBehavior()) { - AppLog.d(AppLog.T.EDITOR, "Overriding predictive text behavior on Samsung device with Samsung Keyboard with API 33") - SamsungInputConnection(this, baseInputConnection) - } else { - baseInputConnection + val inputConnection = requireNotNull(super.onCreateInputConnection(outAttrs)).wrapWithBackSpaceHandler() + + if (shouldOverridePredictiveTextBehavior()) { + AppLog.d(AppLog.T.EDITOR, "Disabling autocorrect on Samsung device with Samsung Keyboard with API 33") + outAttrs.inputType = outAttrs.inputType or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS } + + return inputConnection } private fun InputConnection.wrapWithBackSpaceHandler(): InputConnection { @@ -1838,7 +1840,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown bypassMediaDeletedListener = false } - // removes Grammarly suggestions from default keyboard on Samsung devices on Android 13 (API 33) + // removes auto-correct from default keyboard on Samsung devices on Android 13 (API 33) // Grammarly implementation is often messing spans and cursor position, as described here: // https://github.com/wordpress-mobile/AztecEditor-Android/issues/1023 fun enableSamsungPredictiveBehaviorOverride() { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/SamsungInputConnection.kt b/aztec/src/main/kotlin/org/wordpress/aztec/SamsungInputConnection.kt deleted file mode 100644 index 01b8f3dbd..000000000 --- a/aztec/src/main/kotlin/org/wordpress/aztec/SamsungInputConnection.kt +++ /dev/null @@ -1,187 +0,0 @@ -package org.wordpress.aztec - -import android.os.Build -import android.os.Bundle -import android.text.Editable -import android.text.Selection -import android.text.Spanned -import android.text.style.SuggestionSpan -import android.view.KeyEvent -import android.view.inputmethod.BaseInputConnection -import android.view.inputmethod.CompletionInfo -import android.view.inputmethod.CorrectionInfo -import android.view.inputmethod.ExtractedText -import android.view.inputmethod.ExtractedTextRequest -import android.view.inputmethod.InputConnection -import android.view.inputmethod.InputContentInfo - -/** - * Wrapper around proprietary Samsung InputConnection. Forwards all the calls to it, except for getExtractedText and - * some custom logic in commitText - */ -class SamsungInputConnection( - private val mTextView: AztecText, - private val baseInputConnection: InputConnection -) : BaseInputConnection(mTextView, true) { - - override fun getEditable(): Editable { - return mTextView.editableText - } - - override fun beginBatchEdit(): Boolean { - return baseInputConnection.beginBatchEdit() - } - - override fun endBatchEdit(): Boolean { - return baseInputConnection.endBatchEdit() - } - - override fun clearMetaKeyStates(states: Int): Boolean { - return baseInputConnection.clearMetaKeyStates(states) - } - - override fun sendKeyEvent(event: KeyEvent?): Boolean { - return super.sendKeyEvent(event) - } - - override fun commitCompletion(text: CompletionInfo?): Boolean { - return baseInputConnection.commitCompletion(text) - } - - override fun commitCorrection(correctionInfo: CorrectionInfo?): Boolean { - return baseInputConnection.commitCorrection(correctionInfo) - } - - override fun performEditorAction(actionCode: Int): Boolean { - return baseInputConnection.performEditorAction(actionCode) - } - - override fun performContextMenuAction(id: Int): Boolean { - return baseInputConnection.performContextMenuAction(id) - } - - // Extracted text on Samsung devices on Android 13 is somehow used for Grammarly suggestions which causes a lot of - // issues with spans and cursors. We do not use extracted text, so returning null - // (default behavior of BaseInputConnection) prevents Grammarly from messing up content most of the time - override fun getExtractedText(request: ExtractedTextRequest?, flags: Int): ExtractedText? { - return null - } - - override fun performPrivateCommand(action: String?, data: Bundle?): Boolean { - return baseInputConnection.performPrivateCommand(action, data) - } - - override fun setComposingText(text: CharSequence?, newCursorPosition: Int): Boolean { - return baseInputConnection.setComposingText(text, newCursorPosition) - } - - override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean { - val incomingTextHasSuggestions = text is Spanned && - text.getSpans(0, text.length, SuggestionSpan::class.java).isNotEmpty() - - // Sometime spellchecker tries to commit partial text with suggestions. This mostly works ok, - // but Aztec spans are finicky, and tend to get messed when content of the editor is replaced. - // In this method we do everything replaceText method of EditableInputConnection does, apart from actually - // replacing text. Instead we copy the suggestions from incoming text into editor directly. - if (incomingTextHasSuggestions) { - // delete composing text set previously. - var composingSpanStart = getComposingSpanStart(editable) - var composingSpanEnd = getComposingSpanEnd(editable) - - if (composingSpanEnd < composingSpanStart) { - val tmp = composingSpanStart - composingSpanStart = composingSpanEnd - composingSpanEnd = tmp - } - - if (composingSpanStart != -1 && composingSpanEnd != -1) { - removeComposingSpans(editable) - } else { - composingSpanStart = Selection.getSelectionStart(editable) - composingSpanEnd = Selection.getSelectionEnd(editable) - if (composingSpanStart < 0) composingSpanStart = 0 - if (composingSpanEnd < 0) composingSpanEnd = 0 - if (composingSpanEnd < composingSpanStart) { - val tmp = composingSpanStart - composingSpanStart = composingSpanEnd - composingSpanEnd = tmp - } - } - - var cursorPosition = newCursorPosition - cursorPosition += if (cursorPosition > 0) { - composingSpanEnd - 1 - } else { - composingSpanStart - } - if (newCursorPosition < 0) cursorPosition = 0 - if (newCursorPosition > editable.length) cursorPosition = editable.length - Selection.setSelection(editable, cursorPosition) - - (text as Spanned).getSpans(0, text.length, SuggestionSpan::class.java).forEach { - val st: Int = text.getSpanStart(it) - val en: Int = text.getSpanEnd(it) - val fl: Int = text.getSpanFlags(it) - - if (editable.length > composingSpanStart + en) { - editable.setSpan(it, composingSpanStart + st, composingSpanStart + en, fl) - } - } - - return true - } - return baseInputConnection.commitText(text, newCursorPosition) - } - - override fun commitContent(inputContentInfo: InputContentInfo, flags: Int, opts: Bundle?): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - baseInputConnection.commitContent(inputContentInfo, flags, opts) - } else { - super.commitContent(inputContentInfo, flags, opts) - } - } - - override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean { - return baseInputConnection.deleteSurroundingText(beforeLength, afterLength) - } - - override fun requestCursorUpdates(cursorUpdateMode: Int): Boolean { - return baseInputConnection.requestCursorUpdates(cursorUpdateMode) - } - - override fun reportFullscreenMode(enabled: Boolean): Boolean { - return baseInputConnection.reportFullscreenMode(enabled) - } - - override fun setSelection(start: Int, end: Int): Boolean { - return baseInputConnection.setSelection(start, end) - } - - override fun finishComposingText(): Boolean { - return baseInputConnection.finishComposingText() - } - - override fun setComposingRegion(start: Int, end: Int): Boolean { - return baseInputConnection.setComposingRegion(start, end) - } - - override fun deleteSurroundingTextInCodePoints(beforeLength: Int, afterLength: Int): Boolean { - return baseInputConnection.deleteSurroundingTextInCodePoints(beforeLength, afterLength) - } - - override fun getCursorCapsMode(reqModes: Int): Int { - return baseInputConnection.getCursorCapsMode(reqModes) - } - - override fun getSelectedText(flags: Int): CharSequence? { - return baseInputConnection.getSelectedText(flags) - } - - override fun getTextAfterCursor(length: Int, flags: Int): CharSequence? { - return baseInputConnection.getTextAfterCursor(length, flags) - } - - override fun getTextBeforeCursor(length: Int, flags: Int): CharSequence? { - return baseInputConnection.getTextBeforeCursor(length, flags) - } -} diff --git a/build.gradle b/build.gradle index 34616bff1..48736488a 100644 --- a/build.gradle +++ b/build.gradle @@ -69,6 +69,7 @@ ext { } ext { + // mixed gradlePluginVersion = '3.3.1' kotlinCoroutinesVersion = '1.6.4' tagSoupVersion = '1.2.1' @@ -80,5 +81,9 @@ ext { wordpressUtilsVersion = '3.5.0' espressoVersion = '3.0.1' + // other + wordpressLintVersion = '2.0.0' + + // project aztecProjectDependency = project.hasProperty("aztecVersion") ? "org.wordpress:aztec:${project.getProperty("aztecVersion")}" : project(":aztec") }