Skip to content

Commit 79d476f

Browse files
committed
Add back SamsungInputConnection
1 parent c36ac5f commit 79d476f

File tree

2 files changed

+192
-3
lines changed

2 files changed

+192
-3
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -698,12 +698,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
698698
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
699699
val inputConnection = requireNotNull(super.onCreateInputConnection(outAttrs)).wrapWithBackSpaceHandler()
700700

701-
if (shouldOverridePredictiveTextBehavior()) {
701+
return if (shouldOverridePredictiveTextBehavior()) {
702702
AppLog.d(AppLog.T.EDITOR, "Disabling autocorrect on Samsung device with Samsung Keyboard with API 33")
703703
outAttrs.inputType = outAttrs.inputType or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
704-
}
705704

706-
return inputConnection
705+
SamsungInputConnection(this, inputConnection)
706+
} else {
707+
inputConnection
708+
}
707709
}
708710

709711
private fun InputConnection.wrapWithBackSpaceHandler(): InputConnection {
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package org.wordpress.aztec
2+
3+
import android.os.Build
4+
import android.os.Bundle
5+
import android.text.Editable
6+
import android.text.Selection
7+
import android.text.Spanned
8+
import android.text.style.SuggestionSpan
9+
import android.view.KeyEvent
10+
import android.view.inputmethod.BaseInputConnection
11+
import android.view.inputmethod.CompletionInfo
12+
import android.view.inputmethod.CorrectionInfo
13+
import android.view.inputmethod.ExtractedText
14+
import android.view.inputmethod.ExtractedTextRequest
15+
import android.view.inputmethod.InputConnection
16+
import android.view.inputmethod.InputContentInfo
17+
18+
/**
19+
* Wrapper around proprietary Samsung InputConnection. Forwards all the calls to it, except for getExtractedText and
20+
* some custom logic in commitText
21+
*/
22+
class SamsungInputConnection(
23+
private val mTextView: AztecText,
24+
private val baseInputConnection: InputConnection
25+
) : BaseInputConnection(mTextView, true) {
26+
27+
override fun getEditable(): Editable {
28+
return mTextView.editableText
29+
}
30+
31+
override fun beginBatchEdit(): Boolean {
32+
return baseInputConnection.beginBatchEdit()
33+
}
34+
35+
override fun endBatchEdit(): Boolean {
36+
return baseInputConnection.endBatchEdit()
37+
}
38+
39+
override fun clearMetaKeyStates(states: Int): Boolean {
40+
return baseInputConnection.clearMetaKeyStates(states)
41+
}
42+
43+
override fun sendKeyEvent(event: KeyEvent?): Boolean {
44+
return super.sendKeyEvent(event)
45+
}
46+
47+
override fun commitCompletion(text: CompletionInfo?): Boolean {
48+
return baseInputConnection.commitCompletion(text)
49+
}
50+
51+
override fun commitCorrection(correctionInfo: CorrectionInfo?): Boolean {
52+
return baseInputConnection.commitCorrection(correctionInfo)
53+
}
54+
55+
override fun performEditorAction(actionCode: Int): Boolean {
56+
return baseInputConnection.performEditorAction(actionCode)
57+
}
58+
59+
override fun performContextMenuAction(id: Int): Boolean {
60+
return baseInputConnection.performContextMenuAction(id)
61+
}
62+
63+
// Extracted text on Samsung devices on Android 13 is somehow used for Grammarly suggestions which causes a lot of
64+
// issues with spans and cursors. We do not use extracted text, so returning null
65+
// (default behavior of BaseInputConnection) prevents Grammarly from messing up content most of the time
66+
override fun getExtractedText(request: ExtractedTextRequest?, flags: Int): ExtractedText? {
67+
return null
68+
}
69+
70+
override fun performPrivateCommand(action: String?, data: Bundle?): Boolean {
71+
return baseInputConnection.performPrivateCommand(action, data)
72+
}
73+
74+
override fun setComposingText(text: CharSequence?, newCursorPosition: Int): Boolean {
75+
return baseInputConnection.setComposingText(text, newCursorPosition)
76+
}
77+
78+
override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
79+
val incomingTextHasSuggestions = text is Spanned &&
80+
text.getSpans(0, text.length, SuggestionSpan::class.java).isNotEmpty()
81+
82+
// Sometime spellchecker tries to commit partial text with suggestions. This mostly works ok,
83+
// but Aztec spans are finicky, and tend to get messed when content of the editor is replaced.
84+
// In this method we do everything replaceText method of EditableInputConnection does, apart from actually
85+
// replacing text. Instead we copy the suggestions from incoming text into editor directly.
86+
if (incomingTextHasSuggestions) {
87+
// delete composing text set previously.
88+
var composingSpanStart = getComposingSpanStart(editable)
89+
var composingSpanEnd = getComposingSpanEnd(editable)
90+
91+
if (composingSpanEnd < composingSpanStart) {
92+
val tmp = composingSpanStart
93+
composingSpanStart = composingSpanEnd
94+
composingSpanEnd = tmp
95+
}
96+
97+
if (composingSpanStart != -1 && composingSpanEnd != -1) {
98+
removeComposingSpans(editable)
99+
} else {
100+
composingSpanStart = Selection.getSelectionStart(editable)
101+
composingSpanEnd = Selection.getSelectionEnd(editable)
102+
if (composingSpanStart < 0) composingSpanStart = 0
103+
if (composingSpanEnd < 0) composingSpanEnd = 0
104+
if (composingSpanEnd < composingSpanStart) {
105+
val tmp = composingSpanStart
106+
composingSpanStart = composingSpanEnd
107+
composingSpanEnd = tmp
108+
}
109+
}
110+
111+
var cursorPosition = newCursorPosition
112+
cursorPosition += if (cursorPosition > 0) {
113+
composingSpanEnd - 1
114+
} else {
115+
composingSpanStart
116+
}
117+
if (newCursorPosition < 0) cursorPosition = 0
118+
if (newCursorPosition > editable.length) cursorPosition = editable.length
119+
Selection.setSelection(editable, cursorPosition)
120+
121+
(text as Spanned).getSpans(0, text.length, SuggestionSpan::class.java).forEach {
122+
val st: Int = text.getSpanStart(it)
123+
val en: Int = text.getSpanEnd(it)
124+
val fl: Int = text.getSpanFlags(it)
125+
126+
if (editable.length > composingSpanStart + en) {
127+
editable.setSpan(it, composingSpanStart + st, composingSpanStart + en, fl)
128+
}
129+
}
130+
131+
return true
132+
}
133+
return baseInputConnection.commitText(text, newCursorPosition)
134+
}
135+
136+
override fun commitContent(inputContentInfo: InputContentInfo, flags: Int, opts: Bundle?): Boolean {
137+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
138+
baseInputConnection.commitContent(inputContentInfo, flags, opts)
139+
} else {
140+
super.commitContent(inputContentInfo, flags, opts)
141+
}
142+
}
143+
144+
override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean {
145+
return baseInputConnection.deleteSurroundingText(beforeLength, afterLength)
146+
}
147+
148+
override fun requestCursorUpdates(cursorUpdateMode: Int): Boolean {
149+
return baseInputConnection.requestCursorUpdates(cursorUpdateMode)
150+
}
151+
152+
override fun reportFullscreenMode(enabled: Boolean): Boolean {
153+
return baseInputConnection.reportFullscreenMode(enabled)
154+
}
155+
156+
override fun setSelection(start: Int, end: Int): Boolean {
157+
return baseInputConnection.setSelection(start, end)
158+
}
159+
160+
override fun finishComposingText(): Boolean {
161+
return baseInputConnection.finishComposingText()
162+
}
163+
164+
override fun setComposingRegion(start: Int, end: Int): Boolean {
165+
return baseInputConnection.setComposingRegion(start, end)
166+
}
167+
168+
override fun deleteSurroundingTextInCodePoints(beforeLength: Int, afterLength: Int): Boolean {
169+
return baseInputConnection.deleteSurroundingTextInCodePoints(beforeLength, afterLength)
170+
}
171+
172+
override fun getCursorCapsMode(reqModes: Int): Int {
173+
return baseInputConnection.getCursorCapsMode(reqModes)
174+
}
175+
176+
override fun getSelectedText(flags: Int): CharSequence? {
177+
return baseInputConnection.getSelectedText(flags)
178+
}
179+
180+
override fun getTextAfterCursor(length: Int, flags: Int): CharSequence? {
181+
return baseInputConnection.getTextAfterCursor(length, flags)
182+
}
183+
184+
override fun getTextBeforeCursor(length: Int, flags: Int): CharSequence? {
185+
return baseInputConnection.getTextBeforeCursor(length, flags)
186+
}
187+
}

0 commit comments

Comments
 (0)