From 8db0d2bcac3427f194c73ae42e1d918933da71e1 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Mon, 30 Jun 2025 11:57:28 -0700 Subject: [PATCH] input: add set_ime_editor_info This corresponds to the GameActivity_setImeEditorInfo function on GameActivity. This is not supported on NativeActivity. Signed-off-by: William Casarin --- android-activity/src/game_activity/mod.rs | 23 +- android-activity/src/lib.rs | 224 ++++++++++++++++++++ android-activity/src/native_activity/mod.rs | 8 +- 3 files changed, 253 insertions(+), 2 deletions(-) diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 70b6941..744bfd9 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -27,7 +27,8 @@ use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding}; use crate::jni_utils::{self, CloneJavaVM}; use crate::util::{abort_on_panic, forward_stdio_to_logcat, log_panic, try_get_path_from_ptr}; use crate::{ - AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags, + AndroidApp, ConfigurationRef, ImeOptions, InputStatus, InputType, MainEvent, PollEvent, Rect, + WindowManagerFlags, }; mod ffi; @@ -206,6 +207,22 @@ impl NativeAppGlue { } } + pub fn set_ime_editor_info(&self, input_type: InputType, options: ImeOptions) { + unsafe { + let activity = (*self.as_ptr()).activity; + let action_id = 0; // IME_ACTION_UNSPECIFIED + // (https://developer.android.com/reference/android/view/inputmethod/EditorInfo#IME_ACTION_DONE) + // TODO: expose this later? + + ffi::GameActivity_setImeEditorInfo( + activity, + input_type.bits(), + action_id, + options.bits(), + ); + } + } + // TODO: move into a trait pub fn set_text_input_state(&self, state: TextInputState) { unsafe { @@ -521,6 +538,10 @@ impl AndroidAppInner { self.native_app.set_text_input_state(state); } + pub fn set_ime_editor_info(&self, input_type: InputType, options: ImeOptions) { + self.native_app.set_ime_editor_info(input_type, options); + } + pub(crate) fn device_key_character_map( &self, device_id: i32, diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index 42c207c..45825f9 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -336,6 +336,222 @@ pub enum InputStatus { use activity_impl::AndroidAppInner; pub use activity_impl::AndroidAppWaker; +bitflags! { + /// Flags for [`AndroidApp::set_ime_editor_info`] + /// as per the [android.view.inputmethod.EditorInfo Java API](https://developer.android.com/reference/android/view/inputmethod/EditorInfo) + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct ImeOptions: u32 { + /// If this flag is not set, IMEs will normally replace the "enter" key with the action + /// supplied. This flag indicates that the action should not be available in-line as a + /// replacement for the "enter" key. Typically this is because the action has such a + /// significant impact or is not recoverable enough that accidentally hitting it should be + /// avoided, such as sending a message. + const IME_FLAG_NO_ENTER_ACTION = 1073741824; + + /// Generic unspecified type for ImeOptions + const IME_NULL = 0; + + // TODO: remaining ime flags + } +} + +bitflags! { + /// Flags for [`AndroidApp::set_ime_editor_info`] + /// as per the [android.view.inputmethod.EditorInfo Java API](https://developer.android.com/reference/android/view/inputmethod/EditorInfo) + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct InputType: u32 { + /// Mask of bits that determine the overall class of text being given. Currently + /// supported classes are: TYPE_CLASS_TEXT, TYPE_CLASS_NUMBER, TYPE_CLASS_PHONE, + /// TYPE_CLASS_DATETIME. IME authors: If the class is not one you understand, assume + /// TYPE_CLASS_TEXT with NO variation or flags. + const TYPE_MASK_CLASS = 15; + + /// Mask of bits that determine the variation of the base content class. + const TYPE_MASK_VARIATION = 4080; + + /// Mask of bits that provide addition bit flags of options. + const TYPE_MASK_FLAGS = 16773120; + + /// Special content type for when no explicit type has been specified. 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. + const TYPE_NULL = 0; + + /// Class for normal text. This class supports the following flags (only one of which + /// should be set): TYPE_TEXT_FLAG_CAP_CHARACTERS, TYPE_TEXT_FLAG_CAP_WORDS, and. + /// TYPE_TEXT_FLAG_CAP_SENTENCES. It also supports the following variations: + /// TYPE_TEXT_VARIATION_NORMAL, and TYPE_TEXT_VARIATION_URI. If you do not recognize the + /// variation, normal should be assumed. + const TYPE_CLASS_TEXT = 1; + + /// Flag for TYPE_CLASS_TEXT: capitalize all characters. Overrides + /// #TYPE_TEXT_FLAG_CAP_WORDS} and #TYPE_TEXT_FLAG_CAP_SENTENCES}. This value is + /// explicitly defined to be the same as TextUtils#CAP_MODE_CHARACTERS}. Of + /// course, this only affects languages where there are upper-case and lower-case + /// letters. + const TYPE_TEXT_FLAG_CAP_CHARACTERS = 4096; + + /// Flag for TYPE_CLASS_TEXT: capitalize the first character of every word. + /// Overrides TYPE_TEXT_FLAG_CAP_SENTENCES. This value is explicitly defined + /// to be the same as TextUtils#CAP_MODE_WORDS. Of course, this only affects + /// languages where there are upper-case and lower-case letters. + const TYPE_TEXT_FLAG_CAP_WORDS = 8192; + + /// Flag for TYPE_CLASS_TEXT: capitalize the first character of each sentence. This value + /// is explicitly defined to be the same as TextUtils#CAP_MODE_SENTENCES. For example in + /// English it means to capitalize after a period and a space (note that other languages + /// may have different characters for period, or not use spaces, or use different + /// grammatical rules). Of course, this only affects languages where there are upper-case + /// and lower-case letters. + const TYPE_TEXT_FLAG_CAP_SENTENCES = 16384; + + /// Flag for TYPE_CLASS_TEXT: the user is entering free-form text that should have + /// auto-correction applied to it. Without this flag, the IME will not try to correct + /// typos. You should always set this flag unless you really expect users to type + /// non-words in this field, for example to choose a name for a character in a game. + /// Contrast this with TYPE_TEXT_FLAG_AUTO_COMPLETE and TYPE_TEXT_FLAG_NO_SUGGESTIONS: + /// TYPE_TEXT_FLAG_AUTO_CORRECT means that the IME will try to auto-correct typos as the + /// user is typing, but does not define whether the IME offers an interface to show + /// suggestions. + const TYPE_TEXT_FLAG_AUTO_CORRECT = 32768; + + /// Flag for TYPE_CLASS_TEXT: the text editor (which means the application) is performing + /// auto-completion of the text being entered based on its own semantics, which it will + /// present to the user as they type. This generally means that the input method should + /// not be showing candidates itself, but can expect the editor to supply its own + /// completions/candidates from + /// android.view.inputmethod.InputMethodSession#displayCompletions + /// InputMethodSession.displayCompletions()} as a result of the editor calling + /// android.view.inputmethod.InputMethodManager#displayCompletions + /// InputMethodManager.displayCompletions()}. Note the contrast with + /// TYPE_TEXT_FLAG_AUTO_CORRECT and TYPE_TEXT_FLAG_NO_SUGGESTIONS: + /// TYPE_TEXT_FLAG_AUTO_COMPLETE means the editor should show an interface for displaying + /// suggestions, but instead of supplying its own it will rely on the Editor to pass + /// completions/corrections. + const TYPE_TEXT_FLAG_AUTO_COMPLETE = 65536; + + /// Flag for TYPE_CLASS_TEXT: multiple lines of text can be entered into the + /// field. If this flag is not set, the text field will be constrained to a single + /// line. The IME may also choose not to display an enter key when this flag is not set, + /// as there should be no need to create new lines. + const TYPE_TEXT_FLAG_MULTI_LINE = 131072; + + /// Flag for TYPE_CLASS_TEXT: the regular text view associated with this should + /// not be multi-line, but when a fullscreen input method is providing text it should + /// use multiple lines if it can. + const TYPE_TEXT_FLAG_IME_MULTI_LINE = 262144; + + /// Flag for TYPE_CLASS_TEXT: the input method does not need to display any + /// dictionary-based candidates. This is useful for text views that do not contain words + /// from the language and do not benefit from any dictionary-based completions or + /// corrections. It overrides the TYPE_TEXT_FLAG_AUTO_CORRECT value when set. Please + /// avoid using this unless you are certain this is what you want. Many input methods need + /// suggestions to work well, for example the ones based on gesture typing. Consider + /// clearing TYPE_TEXT_FLAG_AUTO_CORRECT instead if you just do not want the IME to + /// correct typos. Note the contrast with TYPE_TEXT_FLAG_AUTO_CORRECT and + /// TYPE_TEXT_FLAG_AUTO_COMPLETE: TYPE_TEXT_FLAG_NO_SUGGESTIONS means the IME does not + /// need to show an interface to display suggestions. Most IMEs will also take this to + /// mean they do not need to try to auto-correct what the user is typing. + const TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288; + + /// Flag for TYPE_CLASS_TEXT: Let the IME know the text conversion suggestions are + /// required by the application. Text conversion suggestion is for the transliteration + /// languages which has pronunciation characters and target characters. When the user is + /// typing the pronunciation charactes, the IME could provide the possible target + /// characters to the user. When this flag is set, the IME should insert the text + /// conversion suggestions through Builder#setTextConversionSuggestions(List)} and the + /// TextAttribute} with initialized with the text conversion suggestions is provided by + /// the IME to the application. To receive the additional information, the application + /// needs to implement InputConnection#setComposingText(CharSequence, int, + /// TextAttribute)}, InputConnection#setComposingRegion(int, int, TextAttribute)}, and + /// InputConnection#commitText(CharSequence, int, TextAttribute)}. + const TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS = 1048576; + /// Default variation of TYPE_CLASS_TEXT: plain old normal text. + const TYPE_TEXT_VARIATION_NORMAL = 0; + /// Variation of TYPE_CLASS_TEXT: entering a URI. + const TYPE_TEXT_VARIATION_URI = 16; + /// Variation of TYPE_CLASS_TEXT: entering an e-mail address. + const TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 32; + /// Variation of TYPE_CLASS_TEXT: entering the subject line of an e-mail. + const TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 48; + /// Variation of TYPE_CLASS_TEXT: entering a short, possibly informal message such as an instant message or a text message. + const TYPE_TEXT_VARIATION_SHORT_MESSAGE = 64; + /// Variation of TYPE_CLASS_TEXT: entering the content of a long, possibly formal message such as the body of an e-mail. + const TYPE_TEXT_VARIATION_LONG_MESSAGE = 80; + /// Variation of TYPE_CLASS_TEXT: entering the name of a person. + const TYPE_TEXT_VARIATION_PERSON_NAME = 96; + /// Variation of TYPE_CLASS_TEXT: entering a postal mailing address. + const TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 112; + /// Variation of TYPE_CLASS_TEXT: entering a password. + const TYPE_TEXT_VARIATION_PASSWORD = 128; + /// Variation of TYPE_CLASS_TEXT: entering a password, which should be visible to the user. + const TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 144; + /// Variation of TYPE_CLASS_TEXT: entering text inside of a web form. + const TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 160; + /// Variation of TYPE_CLASS_TEXT: entering text to filter contents of a list etc. + const TYPE_TEXT_VARIATION_FILTER = 176; + /// Variation of TYPE_CLASS_TEXT: entering text for phonetic pronunciation, such as a + /// phonetic name field in contacts. This is mostly useful for languages where one + /// spelling may have several phonetic readings, like Japanese. + const TYPE_TEXT_VARIATION_PHONETIC = 192; + /// Variation of TYPE_CLASS_TEXT: entering e-mail address inside of a web form. This + /// was added in android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target this API + /// version or later to see this input type; if it doesn't, a request for this type will + /// be seen as #TYPE_TEXT_VARIATION_EMAIL_ADDRESS} when passed through + /// android.view.inputmethod.EditorInfo#makeCompatible(int) + /// EditorInfo.makeCompatible(int)}. + const TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 208; + /// Variation of TYPE_CLASS_TEXT: entering password inside of a web form. This was + /// added in android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target this API + /// version or later to see this input type; if it doesn't, a request for this type will + /// be seen as #TYPE_TEXT_VARIATION_PASSWORD} when passed through + /// android.view.inputmethod.EditorInfo#makeCompatible(int) + /// EditorInfo.makeCompatible(int)}. + const TYPE_TEXT_VARIATION_WEB_PASSWORD = 224; + /// Class for numeric text. This class supports the following flags: + /// #TYPE_NUMBER_FLAG_SIGNED} and #TYPE_NUMBER_FLAG_DECIMAL}. It also supports the + /// following variations: #TYPE_NUMBER_VARIATION_NORMAL} and + /// #TYPE_NUMBER_VARIATION_PASSWORD}.

IME authors: If you do not recognize the + /// variation, normal should be assumed.

+ const TYPE_CLASS_NUMBER = 2; + /// Flag of TYPE_CLASS_NUMBER: the number is signed, allowing a positive or negative + /// sign at the start. + const TYPE_NUMBER_FLAG_SIGNED = 4096; + /// Flag of TYPE_CLASS_NUMBER: the number is decimal, allowing a decimal point to + /// provide fractional values. + const TYPE_NUMBER_FLAG_DECIMAL = 8192; + /// Default variation of TYPE_CLASS_NUMBER: plain normal numeric text. This was added + /// in android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target this API version or + /// later to see this input type; if it doesn't, a request for this type will be dropped + /// when passed through android.view.inputmethod.EditorInfo#makeCompatible(int) + /// EditorInfo.makeCompatible(int)}. + const TYPE_NUMBER_VARIATION_NORMAL = 0; + /// Variation of TYPE_CLASS_NUMBER: entering a numeric password. This was added in + /// android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target this API version or + /// later to see this input type; if it doesn't, a request for this type will be dropped + /// when passed through android.view.inputmethod.EditorInfo#makeCompatible(int) + /// EditorInfo.makeCompatible(int)}. + const TYPE_NUMBER_VARIATION_PASSWORD = 16; + /// Class for a phone number. This class currently supports no variations or flags. + const TYPE_CLASS_PHONE = 3; + /// Class for dates and times. It supports the following variations: + /// #TYPE_DATETIME_VARIATION_NORMAL} #TYPE_DATETIME_VARIATION_DATE}, and + /// #TYPE_DATETIME_VARIATION_TIME}. + const TYPE_CLASS_DATETIME = 4; + /// Default variation of #TYPE_CLASS_DATETIME}: allows entering both a date and time. + const TYPE_DATETIME_VARIATION_NORMAL = 0; + /// Default variation of #TYPE_CLASS_DATETIME}: allows entering only a date. + const TYPE_DATETIME_VARIATION_DATE = 16; + /// Default variation of #TYPE_CLASS_DATETIME}: allows entering only a time. + const TYPE_DATETIME_VARIATION_TIME = 32; + + } +} + bitflags! { /// Flags for [`AndroidApp::set_window_flags`] /// as per the [android.view.WindowManager.LayoutParams Java API](https://developer.android.com/reference/android/view/WindowManager.LayoutParams) @@ -712,6 +928,14 @@ impl AndroidApp { self.inner.read().unwrap().set_text_input_state(state); } + /// Set IME editor flags + pub fn set_ime_editor_info(&self, input_type: InputType, options: ImeOptions) { + self.inner + .read() + .unwrap() + .set_ime_editor_info(input_type, options); + } + /// Get an exclusive, lending iterator over buffered input events /// /// Applications are expected to call this in-sync with their rendering or diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index 88a9fe8..2a40cdd 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -18,7 +18,8 @@ use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding}; use crate::input::{TextInputState, TextSpan}; use crate::jni_utils::{self, CloneJavaVM}; use crate::{ - util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags, + util, AndroidApp, ConfigurationRef, ImeOptions, InputStatus, InputType, MainEvent, PollEvent, + Rect, WindowManagerFlags, }; pub mod input; @@ -391,6 +392,11 @@ impl AndroidAppInner { // NOP: Unsupported } + // TODO: move into a trait + pub fn set_ime_editor_info(&self, _input_type: InputType, _options: ImeOptions) { + // NOP: Unsupported + } + pub fn device_key_character_map(&self, device_id: i32) -> InternalResult { let mut guard = self.key_maps.lock().unwrap();