|
35 | 35 | import android.view.autofill.AutofillValue; |
36 | 36 | import android.view.inputmethod.EditorInfo; |
37 | 37 | import android.view.inputmethod.InputConnection; |
| 38 | +import android.view.textservice.SpellCheckerInfo; |
| 39 | +import android.view.textservice.TextServicesManager; |
38 | 40 | import android.widget.FrameLayout; |
39 | 41 | import androidx.annotation.NonNull; |
40 | 42 | import androidx.annotation.Nullable; |
|
58 | 60 | import io.flutter.embedding.engine.renderer.RenderSurface; |
59 | 61 | import io.flutter.embedding.engine.systemchannels.SettingsChannel; |
60 | 62 | import io.flutter.plugin.common.BinaryMessenger; |
| 63 | +import io.flutter.plugin.editing.SpellCheckPlugin; |
61 | 64 | import io.flutter.plugin.editing.TextInputPlugin; |
62 | 65 | import io.flutter.plugin.localization.LocalizationPlugin; |
63 | 66 | import io.flutter.plugin.mouse.MouseCursorPlugin; |
@@ -126,10 +129,12 @@ public class FlutterView extends FrameLayout |
126 | 129 | // existing, stateless system channels, e.g., MouseCursorChannel, TextInputChannel, etc. |
127 | 130 | @Nullable private MouseCursorPlugin mouseCursorPlugin; |
128 | 131 | @Nullable private TextInputPlugin textInputPlugin; |
| 132 | + @Nullable private SpellCheckPlugin spellCheckPlugin; |
129 | 133 | @Nullable private LocalizationPlugin localizationPlugin; |
130 | 134 | @Nullable private KeyboardManager keyboardManager; |
131 | 135 | @Nullable private AndroidTouchProcessor androidTouchProcessor; |
132 | 136 | @Nullable private AccessibilityBridge accessibilityBridge; |
| 137 | + @Nullable private TextServicesManager textServicesManager; |
133 | 138 |
|
134 | 139 | // Provides access to foldable/hinge information |
135 | 140 | @Nullable private WindowInfoRepositoryCallbackAdapterWrapper windowInfoRepo; |
@@ -1141,6 +1146,17 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { |
1141 | 1146 | this, |
1142 | 1147 | this.flutterEngine.getTextInputChannel(), |
1143 | 1148 | this.flutterEngine.getPlatformViewsController()); |
| 1149 | + |
| 1150 | + try { |
| 1151 | + textServicesManager = |
| 1152 | + (TextServicesManager) |
| 1153 | + getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); |
| 1154 | + spellCheckPlugin = |
| 1155 | + new SpellCheckPlugin(textServicesManager, this.flutterEngine.getSpellCheckChannel()); |
| 1156 | + } catch (Exception e) { |
| 1157 | + Log.e(TAG, "TextServicesManager not supported by device, spell check disabled."); |
| 1158 | + } |
| 1159 | + |
1144 | 1160 | localizationPlugin = this.flutterEngine.getLocalizationPlugin(); |
1145 | 1161 |
|
1146 | 1162 | keyboardManager = new KeyboardManager(this); |
@@ -1238,6 +1254,9 @@ public void detachFromFlutterEngine() { |
1238 | 1254 | textInputPlugin.getInputMethodManager().restartInput(this); |
1239 | 1255 | textInputPlugin.destroy(); |
1240 | 1256 | keyboardManager.destroy(); |
| 1257 | + if (spellCheckPlugin != null) { |
| 1258 | + spellCheckPlugin.destroy(); |
| 1259 | + } |
1241 | 1260 |
|
1242 | 1261 | if (mouseCursorPlugin != null) { |
1243 | 1262 | mouseCursorPlugin.destroy(); |
@@ -1422,10 +1441,34 @@ public void removeFlutterEngineAttachmentListener( |
1422 | 1441 | ? SettingsChannel.PlatformBrightness.dark |
1423 | 1442 | : SettingsChannel.PlatformBrightness.light; |
1424 | 1443 |
|
| 1444 | + boolean isNativeSpellCheckServiceDefined = false; |
| 1445 | + |
| 1446 | + if (textServicesManager != null) { |
| 1447 | + if (Build.VERSION.SDK_INT >= 31) { |
| 1448 | + List<SpellCheckerInfo> enabledSpellCheckerInfos = |
| 1449 | + textServicesManager.getEnabledSpellCheckerInfos(); |
| 1450 | + boolean gboardSpellCheckerEnabled = |
| 1451 | + enabledSpellCheckerInfos.stream() |
| 1452 | + .anyMatch( |
| 1453 | + spellCheckerInfo -> |
| 1454 | + spellCheckerInfo |
| 1455 | + .getPackageName() |
| 1456 | + .equals("com.google.android.inputmethod.latin")); |
| 1457 | + |
| 1458 | + // Checks if enabled spell checker is the one that is suppported by Gboard, which is |
| 1459 | + // the one Flutter supports by default. |
| 1460 | + isNativeSpellCheckServiceDefined = |
| 1461 | + textServicesManager.isSpellCheckerEnabled() && gboardSpellCheckerEnabled; |
| 1462 | + } else { |
| 1463 | + isNativeSpellCheckServiceDefined = true; |
| 1464 | + } |
| 1465 | + } |
| 1466 | + |
1425 | 1467 | flutterEngine |
1426 | 1468 | .getSettingsChannel() |
1427 | 1469 | .startMessage() |
1428 | 1470 | .setTextScaleFactor(getResources().getConfiguration().fontScale) |
| 1471 | + .setNativeSpellCheckServiceDefined(isNativeSpellCheckServiceDefined) |
1429 | 1472 | .setBrieflyShowPassword( |
1430 | 1473 | Settings.System.getInt( |
1431 | 1474 | getContext().getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 1) |
|
0 commit comments