Losing spans on EditText with Google Keyboard as input - android

I'm trying to apply ForegroundColorSpan with Spannable.EXCLUSIVE_INCLUSIVE flag when using TextWatcher at a specific index range.
With my device's default input (Samsung Keyboard) there are no issues and everything works as expected, however when using Google Keyboard as input that's a different story.
From my debugging I found that Google Keyboard places whole words, even if you change one character - I guess that's because of the word correction feature. As a result, if the Span was applied at some point in the word (even at the end), and a user enters more text, the span disappears.
The only exception is when the span is applied to the last character in the word, and the user enters a space ("ends" the word).
The workaround I found is to simply check if the input is a whole word, and whenever it is - apply the Span again. While that does work, it's not a very good solution since I need to set the caret at the end every time, and the fact that I have to re-apply the spin when it could be avoided (that's why I use Spannable.EXCLUSIVE_INCLUSIVE flag).
P.S. I tried other flags such as INCLUSIVE_INCLUSIVE and it sorta works - however it applies the span backwards as-well, resulting the Span to be applied to the entire word, rather than the index I specified.
Are there better solutions to this issue?

Okay so I found a different solution - turning off keyboard's input suggestion. Doing so prevents the input from being whole word and instead just a single character every time.
To do so I had to add textNoSuggestions flag to EditText's inputType attribute.
android:inputType="textNoSuggestions|<other_flags>"
I found more information about this inputType here: Turn off autosuggest for EditText?

Related

Set composing text on an EditText from a custom keyboard in Android

Explanation of what I am trying to do
I'm making a custom in-app keyboard that works on the same principle of this example. However, in my keyboard I'm using popup windows to display extra letter forms. In traditional Mongolian letters have different forms depending on if they are located at the beginning, middle, or end of a word. Usually these can be determined from the context, but sometimes a user needs to choose an alternate form explicitly from the popup key candidates.
Lets say that a user starts typing a word (where - represent letters):
---
Then they chose a from the popup (I'm only using a to represent the concept of choosing a special Mongolian glyph form). The Unicode for this letter will be rendered as follows if they continue typing:
---a--
However, the Unicode is rendered as
---A
at the end of words. (a and A have the same code in Unicode.) So the user is confused why they chose a from the popup key but it gets rendered as A in the editor. If they would just keep typing, though, it will be fine since it gets rendered as a in middle of words.
What I want to do it set some sort of temporary span on the a of ---a so that it doesn't get rendered as ---A before they type the next letter. But if they add a space or move the cursor to a different location, then it will revert back to the default ---A form for final letters. (That is, the temporary span will be cancelled.)
Real example
If the abstract a and A are too confusing, here is a real example. The user wants to type a Mongolian UE form (Unicode \u1826\u180c) in this word
But since \u1826\u180c gets rendered like this at the end of words
the user is confused until they continue typing. I want the span to make it look like this
which can be temporarily rendered with \u1826\u180c\u200d.
Documentation
The documentation says
If your IME does text prediction or requires multiple steps to compose
a glyph or word, you can show the progress in the text field until the
user commits the word, and then you can replace the partial
composition with the completed text.
And it gives this example and image:
InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
ic.setComposingText("Composin", 1);
ic.commitText("Composing ", 1);
Problem
I was going to describe why it wasn't working in this section, but in the process of setting up my question here, I discovered that it actually works. So I will add my answer below as an example for other people are who doing something similar.
The following code sets a temporary composing span when the string in question is returned by the popup window
if (selectedItem.equals("a")) {
inputConnection.setComposingText("a", 1);
}
where selectedItem is the string chosen by the user from the key popup window candidates.
Note that the a has an underline indicating that it is a composing span. (This is a contrived example from the question where a would be rendered as A if the text were committed immediately.)
This also works for the real example in the question
if (selectedItem.equals("\u1826\u180c")) {
inputConnection.setComposingText("\u1826\u180c\u200d", 1);
}
Committing the composing span
When it is confirmed that the user wants to keep the composing span (ie, they keep typing more letters in the word), it can be committed with
inputConnection.commitText("\u1826\u180c", 1);
Abandoning the composing span
If the user clicks somewhere else, the composing span is not cancelled. But this is a different question.
Your keyboard can override onUpdateSelection to listen for cursor changes there. Then call
inputConnection.finishComposingText();
to keep whatever text was in the composing region. Or
ic.commitText("", 1);
to get rid of it. See this answer for more.

Android use enter key to navigate to next EditText with first letter capitalization

I know there are several posts about facilitating navigation between EditText fields. However, I can't get the suggested solutions to work and still have my EditTexts automatically have the first letter be capitalized. While setting the input type to "text" accomplishes what I want, if I set it to "textCapSentences", even using imeOptions, nextFocus, etc., I can't get the behavior I'm looking for.

What are the semantics of each InputType constant?

You can set a TextView's inputType to one of the values from InputType to hint that the typed-in text should be a person's name, phone number, &c. Even if the input method doesn't respect this hint, the TextView uses a KeyListener and/or TransformationMethod to ensure that only relevant characters can be entered, or to have effects like masking the password. Even the flags are more than just hints: they can change the behaviour of the TextView significantly (the most obvious example being EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE).
Google's documentation is very vague about the actual effect of each inputType. What characters are actually permitted in each case? How does this vary by locale, if at all? Even if there is no documented answer, and it's liable to change between versions, I'd still like to know the expected behaviour.
You can find this out by inspecting the source of the *KeyListener classes, though of course this may be changed in different versions or by manufacturer or carrier customizations. The below is based on the AOSP 4.3 source. These are only the effects that each type has on Android itself: input methods also use the types as hints to better predict what the user is likely to type. For example, although TYPE_TEXT_VARIATION_PERSON_NAME only has the effect of disabling spell-check, the IM might respond to this type by auto-completing from a dictionary of common names instead of from a language dictionary.
To experiment with input types and IME options, I hacked up a quick app that lets you select them from a list in a GUI, so you don't have to edit an XML layout and rebuild an app to do it. If you want to find out more, or check how they interact with a given IM app, download IM prove free from Google Play.
TYPE_NULL
This one is actually documented:
This should be interpreted to mean that the target input connection is not rich, it can not process and show things like candidate text nor retrieve the current text, so the input method will need to run in a limited "generate key events" mode, if it supports it. Note that some input methods may not support it, for example a voice-based input method will likely not be able to generate key events even if this flag is set.
This makes it sounds like it's for cases where you're not editing text, but pressing a key on the IM will do some action directly. But in fact it hides the IM completely. From the app's point of view, you almost never want this: set it if you only want a hardware keyboard to be able to enter text.
From the IM's point of view, you'll get this type passed to onStartInput a lot, usually when Android is about to hide the IM because a different activity is coming to the foreground. You want responding to this input type to be fast. There could be two reasons why it works this way, but someone involved with the design would have to confirm why:-
It could be to let the IM know that editing is completely finished in that window (unlike onFinishInput, which merely means the IM is being hidden), so it can free memory used for dictionaries and the like until editing restarts.
It could be part of what allows a hardware keyboard to use arrow keys for scrolling, menu accelerator keys, and so on, even when no text input is taking place.
Classes
Numeric types
TYPE_CLASS_NUMBER gives you the digits 0-9. In addition, adding TYPE_NUMBER_FLAG_SIGNED lets you have + or -, but only as the first character. Adding TYPE_NUMBER_FLAG_DECIMAL lets you have . in any position, but only once. You can have both signed and decimal. As far as I can tell, this isn't localized, so the allowed characters are the same even for locales where . is the thousands separator and , is the decimal point, or for locales with different number characters.
TYPE_CLASS_PHONE lets you have the digits 0-9, as well as any of #*+-(),/N.; and space. You can have those characters in any order and any number of times: there's no formatting check.
TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_DATE lets you have the digits 0-9 as well as any of /-.. Again, there's no extra check for formatting, so you can have them in any order.
TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_TIME lets you have the digits 0-9 as well as : and any of amp (for writing "am" or "pm", but you can use them in any order and position). Slightly perversely, you can't have space or . for "3 pm" or "2 p.m." or even "2.30". Again, it doesn't seem to be localized.
TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_NORMAL gets you 0-9 as well as :/-, space, and amp. This notably doesn't include . even though it's allowed in a date.
Based on the above observations, I can't say I'd recommend using any of the above classes. They all seem to have major absences and prevent localization. The above are the only classes with character restrictions.
TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD does as you'd expect: it uses a TransformationMethod to obscure the typed characters.
Text types
In TYPE_CLASS_TEXT, setting TYPE_TEXT_VARIATION_EMAIL_ADDRESS or TYPE_TEXT_VARIATION_EMAIL_SUBJECT makes a press of the enter key move focus to the next field instead of inserting a newline.
TYPE_TEXT_VARIATION_FILTER will prevent the input method going to full-screen (extract) mode.
TYPE_TEXT_VARIATION_PASSWORD has the obvious effect: it uses a TransformationMethod to obscure the typed characters. TYPE_TEXT_VARIATION_VISIBLE_PASSWORD still uses the TransformationMethod to prevent the text being copied
All of the following text variations allow spell-checking if TYPE_TEXT_FLAG_NO_SUGGESTIONS is not set. That is, using an class that is not TYPE_CLASS_TEXT or a variation that is not in this list has the same effect as setting TYPE_TEXT_FLAG_NO_SUGGESTIONS (which is described later).
TYPE_TEXT_VARIATION_NORMAL
TYPE_TEXT_VARIATION_EMAIL_SUBJECT
TYPE_TEXT_VARIATION_LONG_MESSAGE
TYPE_TEXT_VARIATION_SHORT_MESSAGE
TYPE_TEXT_VARIATION_WEB_EDIT_TEXT
Flags
The presence or absence of InputType.TYPE_TEXT_FLAG_MULTI_LINE has non-obvious side-effects. If the type class is not TYPE_CLASS_TEXT, it is always as if the flag were not set, and the TextView goes into single-line mode. Setting lines or maxLines to 1 only affects the way the text is displayed: it does not start single-line mode.
In single-line mode:-
the ellipsize option defaults to end
pressing enter performs the "editor action" or moves focus to the next field (just as for email addresses or subjects, above); otherwise, it inserts a newline
pressing tab moves focus to the next field, only if TYPE_TEXT_FLAG_IME_MULTI_LINE is not set; otherwise it inserts a tab character
the imeOptions can include an "editor action" to replace the enter key on a soft keyboard; in multi-line mode, TextView will add IME_FLAG_NO_ENTER_ACTION to the imeOptions
maxLines is automatically set to 1
adding a newline to the field (e.g. using setText) has no effect, and a carriage return is replaced with a zero-width space (U+FEFF)
TYPE_TEXT_FLAG_CAP_* use TextUtils.getCapsMode to decide whether to capitalize each character. The rules are a little baroque and are not locale-sensitive. AFAICT, this only takes effect if the corresponding setting is enabled in the default keyboard.
When TYPE_TEXT_FLAG_AUTO_CORRECT is set, space, tab, newline, any Unicode "end punctuation" character, or any of ,.!?" will trigger auto-correction of the preceding word (in this context, that's the longest sequence of Unicode letters and apostrophes). If the whole word doesn't have a correction, it continues to retry with shorter subsequences. The corrections come from a fixed system resource com.android.internal.R.xml.autotext and are separate from any configured spell-checker.
TYPE_TEXT_FLAG_NO_SUGGESTIONS (or the text variations listed earlier) stops the text being spell-checked. This prevents the spell-check suggestions list being shown, and also stops misspelled words being highlighted. The input method can still provide completions if it chooses.
Other oddities
Long-tapping a TextView usually selects the tapped word, but if the input type is one of the following, it selects all of the text instead:-
TYPE_CLASS_NUMBER
TYPE_CLASS_PHONE
TYPE_CLASS_DATETIME (any variation)
TYPE_TEXT_VARIATION_URI
TYPE_TEXT_VARIATION_EMAIL_ADDRESS
TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS
TYPE_TEXT_VARIATION_FILTER

How do I determine if highlight/underline in EditText is from autocomplete

I have a custom view that extends EditText that has ToggleButtons for rich text editing. If I allow autocomplete, which I want to do, the indicator for the current word triggers my detection for style spans.
For example on most devices the autocorrect eligible word is an underline. As you type I have a text watcher that keeps track of the current styles that are applied to the text and adds new spannables if the user toggles a style button. This ends up detecting the underline and turning the toggle on.
I can write code to check if the underline toggle was set before we found the span. (I would actually need to do this for all my styles really since some devices use a background color to indicate the current autocorrect word.) But I'm unsure what I would use to trigger turning the toggle back to off. Check if they typed space? What happens when you select a suggested word?
Has anyone done this? Is there a way to ask if the span is from autocomplete or any other notifications to know the OS drew the span?
This is an old question, but just now I had a similar problem, namely eliminating the unwanted underline before converting the spans into HTML. I found the answer in the source to TextView.
There is a method TextView#clearComposingText() that will remove all styles applied by the IME during autosuggest, preserving all other styles. You could likely call it after every user keystroke, which would remove the underlines.
Hope this helps someone.

UPDATE: This is now a Swype related issue: Disable the automatic capitalizing of a sentence's first character in an EditText

I'm implementing an EditText that is used to obtain an rtsp URI from the user. Though it's not a huge issue functionally, I would like to disable the default behavior of the on screen keyboard where the state of the caps key is enabled for the first character of every sentence. From what I've gathered, this cannot be done in the XML via android:inputType flags. Any pointers as to how to do this programatically would be helpful.
I'd like to include the code, but the nature of the project prevents me from doing so. But I can say that nothing fancy is being done here. The EditText itself has no other input flags in use, and the only IME option in place is the flag to disable the Extract UI in landscape. The layout is implemented via XML, not programatically, so there are no custom classes or extensions in the mix either.
EDIT: The android:capitalize constant inherited from TextView isn't working. I'm specifically targetting 2.2 on a Galaxy Tab, so I don't know if this has something to do with Samsung's OS tweaks, or if it has something to do with the capitalize constant being deprecated.
EDIT 2: Swype is the culprit for not allowing the use of the capitalize constant. Does anyone know if there is a way to make Swype play nice without having to inform the end user to disable Swype?
You can use the inherited android:capitalize attribute from TextView. It accepts none (no caps, which is what you want), sentences (capitalizes the first word of each sentence), words (capitalizes the first letter of every word), and character (all caps).
Putting this in the XML for the EditText worked to stop Swype 2.17.59.15552.t100 from forcing capitals:
android:inputType="text|textEmailAddress"
Since EditText is subclass of TextView, you should be able to use the TextView:capitalize configuration to change the capitalization scheme of the view. It looks like you might only be able to set none in xml.

Categories

Resources