Skip to content

Commit 1ba506f

Browse files
authored
Revert "Samsung keyboard duplication workaround: updateSelection (flutter#16547)"
This reverts commit df3f267.
1 parent 8ef05bd commit 1ba506f

File tree

2 files changed

+8
-140
lines changed

2 files changed

+8
-140
lines changed

shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
package io.flutter.plugin.editing;
66

7-
import android.annotation.SuppressLint;
87
import android.content.Context;
98
import android.os.Build;
10-
import android.provider.Settings;
119
import android.text.DynamicLayout;
1210
import android.text.Editable;
1311
import android.text.Layout;
@@ -19,7 +17,6 @@
1917
import android.view.inputmethod.CursorAnchorInfo;
2018
import android.view.inputmethod.EditorInfo;
2119
import android.view.inputmethod.InputMethodManager;
22-
import android.view.inputmethod.InputMethodSubtype;
2320
import io.flutter.Log;
2421
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
2522

@@ -33,9 +30,6 @@ class InputConnectionAdaptor extends BaseInputConnection {
3330
private InputMethodManager mImm;
3431
private final Layout mLayout;
3532

36-
// Used to determine if Samsung-specific hacks should be applied.
37-
private final boolean isSamsung;
38-
3933
@SuppressWarnings("deprecation")
4034
public InputConnectionAdaptor(
4135
View view,
@@ -62,8 +56,6 @@ public InputConnectionAdaptor(
6256
0.0f,
6357
false);
6458
mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
65-
66-
isSamsung = isSamsung();
6759
}
6860

6961
// Send the current state of the editable to Flutter.
@@ -140,64 +132,19 @@ public boolean setComposingText(CharSequence text, int newCursorPosition) {
140132
public boolean finishComposingText() {
141133
boolean result = super.finishComposingText();
142134

143-
// Apply Samsung hacks. Samsung caches composing region data strangely, causing text
144-
// duplication.
145-
if (isSamsung) {
146-
if (Build.VERSION.SDK_INT >= 21) {
147-
// Samsung keyboards don't clear the composing region on finishComposingText.
148-
// Update the keyboard with a reset/empty composing region. Critical on
149-
// Samsung keyboards to prevent punctuation duplication.
150-
CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
151-
builder.setComposingText(/*composingTextStart*/ -1, /*composingText*/ "");
152-
CursorAnchorInfo anchorInfo = builder.build();
153-
mImm.updateCursorAnchorInfo(mFlutterView, anchorInfo);
154-
}
155-
// TODO(garyq): There is still a duplication case that comes from hiding+showing the keyboard.
156-
// The exact behavior to cause it has so far been hard to pinpoint and it happens far more
157-
// rarely than the original bug.
158-
159-
// Temporarily indicate to the IME that the composing region selection should be reset.
160-
// The correct selection is then immediately set properly in the updateEditingState() call
161-
// in this method. This is a hack to trigger Samsung keyboard's internal cache to clear.
162-
// This prevents duplication on keyboard hide+show. See
163-
// https://github.com/flutter/flutter/issues/31512
164-
//
165-
// We only do this if the proper selection will be restored later, eg, when mBatchCount is 0.
166-
if (mBatchCount == 0) {
167-
mImm.updateSelection(
168-
mFlutterView,
169-
-1, /*selStart*/
170-
-1, /*selEnd*/
171-
-1, /*candidatesStart*/
172-
-1 /*candidatesEnd*/);
173-
}
135+
if (Build.VERSION.SDK_INT >= 21) {
136+
// Update the keyboard with a reset/empty composing region. Critical on
137+
// Samsung keyboards to prevent punctuation duplication.
138+
CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
139+
builder.setComposingText(-1, "");
140+
CursorAnchorInfo anchorInfo = builder.build();
141+
mImm.updateCursorAnchorInfo(mFlutterView, anchorInfo);
174142
}
175143

176144
updateEditingState();
177145
return result;
178146
}
179147

180-
// Detect if the keyboard is a Samsung keyboard, where we apply Samsung-specific hacks to
181-
// fix critical bugs that make the keyboard otherwise unusable. See finishComposingText() for
182-
// more details.
183-
@SuppressLint("NewApi") // New API guard is inline, the linter can't see it.
184-
@SuppressWarnings("deprecation")
185-
private boolean isSamsung() {
186-
InputMethodSubtype subtype = mImm.getCurrentInputMethodSubtype();
187-
// Impacted devices all shipped with Android Lollipop or newer.
188-
if (subtype == null
189-
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
190-
|| !Build.MANUFACTURER.equals("samsung")) {
191-
return false;
192-
}
193-
String keyboardName =
194-
Settings.Secure.getString(
195-
mFlutterView.getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
196-
// The Samsung keyboard is called "com.sec.android.inputmethod/.SamsungKeypad" but look
197-
// for "Samsung" just in case Samsung changes the name of the keyboard.
198-
return keyboardName.contains("Samsung");
199-
}
200-
201148
@Override
202149
public boolean setSelection(int start, int end) {
203150
boolean result = super.setSelection(start, end);

shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@
2828
import io.flutter.plugin.common.MethodCall;
2929
import io.flutter.plugin.platform.PlatformViewsController;
3030
import java.nio.ByteBuffer;
31-
import java.util.ArrayList;
32-
import java.util.Arrays;
33-
import java.util.List;
3431
import org.json.JSONArray;
3532
import org.json.JSONException;
3633
import org.junit.Test;
@@ -308,17 +305,9 @@ public void inputConnection_createsActionFromEnter() throws JSONException {
308305

309306
@Test
310307
public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException {
311-
ShadowBuild.setManufacturer("samsung");
312-
InputMethodSubtype inputMethodSubtype =
313-
new InputMethodSubtype(0, 0, /*locale=*/ "en", "", "", false, false);
314-
Settings.Secure.putString(
315-
RuntimeEnvironment.application.getContentResolver(),
316-
Settings.Secure.DEFAULT_INPUT_METHOD,
317-
"com.sec.android.inputmethod/.SamsungKeypad");
318308
TestImm testImm =
319309
Shadow.extract(
320310
RuntimeEnvironment.application.getSystemService(Context.INPUT_METHOD_SERVICE));
321-
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
322311
FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
323312
View testView = new View(RuntimeEnvironment.application);
324313
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
@@ -349,59 +338,13 @@ public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException
349338
}
350339
}
351340

352-
@Test
353-
public void inputConnection_samsungFinishComposingTextSetsSelection() throws JSONException {
354-
ShadowBuild.setManufacturer("samsung");
355-
InputMethodSubtype inputMethodSubtype =
356-
new InputMethodSubtype(0, 0, /*locale=*/ "en", "", "", false, false);
357-
Settings.Secure.putString(
358-
RuntimeEnvironment.application.getContentResolver(),
359-
Settings.Secure.DEFAULT_INPUT_METHOD,
360-
"com.sec.android.inputmethod/.SamsungKeypad");
361-
TestImm testImm =
362-
Shadow.extract(
363-
RuntimeEnvironment.application.getSystemService(Context.INPUT_METHOD_SERVICE));
364-
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
365-
FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
366-
View testView = new View(RuntimeEnvironment.application);
367-
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
368-
TextInputPlugin textInputPlugin =
369-
new TextInputPlugin(testView, dartExecutor, mock(PlatformViewsController.class));
370-
textInputPlugin.setTextInputClient(
371-
0,
372-
new TextInputChannel.Configuration(
373-
false,
374-
false,
375-
true,
376-
TextInputChannel.TextCapitalization.NONE,
377-
new TextInputChannel.InputType(TextInputChannel.TextInputType.TEXT, false, false),
378-
null,
379-
null));
380-
// There's a pending restart since we initialized the text input client. Flush that now.
381-
textInputPlugin.setTextInputEditingState(
382-
testView, new TextInputChannel.TextEditState("", 0, 0));
383-
InputConnection connection = textInputPlugin.createInputConnection(testView, new EditorInfo());
384-
385-
testImm.setTrackSelection(true);
386-
connection.finishComposingText();
387-
testImm.setTrackSelection(false);
388-
389-
List<Integer> expectedSelectionValues =
390-
Arrays.asList(0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1);
391-
assertEquals(testImm.getSelectionUpdateValues(), expectedSelectionValues);
392-
}
393-
394341
@Implements(InputMethodManager.class)
395342
public static class TestImm extends ShadowInputMethodManager {
396343
private InputMethodSubtype currentInputMethodSubtype;
397344
private SparseIntArray restartCounter = new SparseIntArray();
398345
private CursorAnchorInfo cursorAnchorInfo;
399-
private ArrayList<Integer> selectionUpdateValues;
400-
private boolean trackSelection = false;
401346

402-
public TestImm() {
403-
selectionUpdateValues = new ArrayList<Integer>();
404-
}
347+
public TestImm() {}
405348

406349
@Implementation
407350
public InputMethodSubtype getCurrentInputMethodSubtype() {
@@ -427,28 +370,6 @@ public void updateCursorAnchorInfo(View view, CursorAnchorInfo cursorAnchorInfo)
427370
this.cursorAnchorInfo = cursorAnchorInfo;
428371
}
429372

430-
// We simply store the values to verify later.
431-
@Implementation
432-
public void updateSelection(
433-
View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) {
434-
if (trackSelection) {
435-
this.selectionUpdateValues.add(selStart);
436-
this.selectionUpdateValues.add(selEnd);
437-
this.selectionUpdateValues.add(candidatesStart);
438-
this.selectionUpdateValues.add(candidatesEnd);
439-
}
440-
}
441-
442-
// only track values when enabled via this.
443-
public void setTrackSelection(boolean val) {
444-
trackSelection = val;
445-
}
446-
447-
// Returns true if the last updateSelection call passed the following values.
448-
public ArrayList<Integer> getSelectionUpdateValues() {
449-
return selectionUpdateValues;
450-
}
451-
452373
public CursorAnchorInfo getLastCursorAnchorInfo() {
453374
return cursorAnchorInfo;
454375
}

0 commit comments

Comments
 (0)