Repository: beeware/toga

Issue:
Android Read-only Multi-line text input displays suggestions (Spell Checking)
### Describe the bug

When using the "MultilineTextInput" with the "readonly" parameter set to "True" on Android, it will suggest spelling improvements based on dictionary candidates. This behavior results in underlined text on android devices when using a language other than the keyboard language or with unknown words in general.

### Steps to reproduce

1. Use MultilineTextInput in readonly with a set text
2. Compile for android and set the KeyBoard language to a different one than the text.
3. The suggestions should appear, underlining the Text.

### Expected behavior

Suggestions do not show up in MultilineTextInput if set to readonly.

### Screenshots

_No response_

### Environment

- Operating System: Fedora 38 Workstation / Lineage OS 20.0
- Python version: 3.11.5
- Software versions:
  - Briefcase: 0.3.15
  - Toga: 0.3.1 

[tool.briefcase.app.trinker.android]
requires = [
    #"toga-android~=0.3.1"
    'git+https://github.com/beeware/toga.git#subdirectory=core',
    'git+https://github.com/beeware/toga.git#subdirectory=android',
]



### Logs

```

```


### Additional context

I did some research on the topic and while I'm not really an Android dev, I think I found the problem. In "android/src/toga_android/widgets/textinput.py" we need to set the flag [TYPE_TEXT_FLAG_NO_SUGGESTIONS](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS) if "readonly" is true.

As said before, I'm not really an Android dev and am not sure how this would be implemented.



Discussion:
For anyone interested in tackling this: `TYPE_TEXT_FLAG_NO_SUGGESTIONS` is a constant on the same interface as `TYPE_CLASS_TEXT`, the InputType that the Android TextInput already uses (see the [`create()`](https://github.com/beeware/toga/blob/main/android/src/toga_android/widgets/textinput.py#L64) method). Add a call to add/remove the no-suggestions constant as part of the [`set_readonly()` implementation](https://github.com/beeware/toga/blob/main/android/src/toga_android/widgets/textinput.py#L80), and add validation that the property has been set in the [test backend probe](https://github.com/beeware/toga/blob/main/android/tests_backend/widgets/textinput.py#L36), and you're done.

Resolving patch:
diff --git a/android/src/toga_android/widgets/textinput.py b/android/src/toga_android/widgets/textinput.py
--- a/android/src/toga_android/widgets/textinput.py
+++ b/android/src/toga_android/widgets/textinput.py
@@ -77,9 +77,19 @@ def set_readonly(self, readonly):
         if readonly:
             # Implicitly calls setFocusableInTouchMode(False)
             self.native.setFocusable(False)
+            # Add TYPE_TEXT_FLAG_NO_SUGGESTIONS to the input type to disable suggestions
+            input_type = (
+                self.native.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+            )
+            self.native.setInputType(input_type)
         else:
             # Implicitly calls setFocusable(True)
             self.native.setFocusableInTouchMode(True)
+            # Remove TYPE_TEXT_FLAG_NO_SUGGESTIONS to enable suggestions
+            input_type = (
+                self.native.getInputType() & ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+            )
+            self.native.setInputType(input_type)
 
     def get_placeholder(self):
         return str(self.native.getHint())


Test patch:
diff --git a/android/tests_backend/widgets/textinput.py b/android/tests_backend/widgets/textinput.py
--- a/android/tests_backend/widgets/textinput.py
+++ b/android/tests_backend/widgets/textinput.py
@@ -38,6 +38,22 @@ def readonly(self):
         focusable_in_touch_mode = self.native.isFocusableInTouchMode()
         if focusable != focusable_in_touch_mode:
             raise ValueError(f"invalid state: {focusable=}, {focusable_in_touch_mode=}")
+
+        # Check if TYPE_TEXT_FLAG_NO_SUGGESTIONS is set in the input type
+        input_type = self.native.getInputType()
+        if input_type & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS:
+            # TYPE_TEXT_FLAG_NO_SUGGESTIONS is set
+            if focusable:
+                raise ValueError(
+                    "TYPE_TEXT_FLAG_NO_SUGGESTIONS is not set on the input."
+                )
+        else:
+            # TYPE_TEXT_FLAG_NO_SUGGESTIONS is not set
+            if not focusable:
+                raise ValueError(
+                    "TYPE_TEXT_FLAG_NO_SUGGESTIONS has been set when the input is readonly."
+                )
+
         return not focusable
 
     async def type_character(self, char):