diff --git a/Android~/plugin/src/main/java/com/mopsicus/umi/MobileInput.java b/Android~/plugin/src/main/java/com/mopsicus/umi/MobileInput.java index 2cf4ec6..840a882 100644 --- a/Android~/plugin/src/main/java/com/mopsicus/umi/MobileInput.java +++ b/Android~/plugin/src/main/java/com/mopsicus/umi/MobileInput.java @@ -1,6 +1,7 @@ package com.mopsicus.umi; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.PorterDuff; @@ -10,6 +11,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; +import android.os.LocaleList; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -30,6 +32,10 @@ import org.json.JSONObject; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; public class MobileInput { @@ -46,6 +52,7 @@ public class MobileInput { private static final String ON_FOCUS = "ON_FOCUS"; private static final String ON_UNFOCUS = "ON_UNFOCUS"; private static final String SET_VISIBLE = "SET_VISIBLE"; + private static final String SET_LANGUAGE = "SET_LANGUAGE"; private static final String TEXT_CHANGE = "TEXT_CHANGE"; private static final String TEXT_END_EDIT = "TEXT_END_EDIT"; private static final String ANDROID_KEY_DOWN = "ANDROID_KEY_DOWN"; @@ -198,6 +205,10 @@ private void processData(JSONObject data) { boolean isVisible = data.getBoolean("is_visible"); this.SetVisible(isVisible); break; + case SET_LANGUAGE: + String code = data.getString("value"); + setKeyboardLanguage(code); + break; case ANDROID_KEY_DOWN: String strKey = data.getString("key"); this.OnForceAndroidKeyDown(strKey); @@ -285,6 +296,7 @@ private void Create(int id, JSONObject data) { String contentType = data.getString("content_type"); String inputType = data.optString("input_type"); String keyboardType = data.optString("keyboard_type"); + String keyboardLanguage = data.optString("keyboard_language"); String returnKeyType = data.getString("return_key_type"); String alignment = data.getString("align"); String customFont = data.getString("font"); @@ -417,6 +429,9 @@ private void Create(int id, JSONObject data) { } else { edit.setTypeface(Typeface.SANS_SERIF); } + if (!keyboardLanguage.equals("default")) { + setKeyboardLanguage(keyboardLanguage); + } final MobileInput input = this; edit.setOnFocusChangeListener((v, isFocus) -> { if (!isFocus) { @@ -517,25 +532,33 @@ void setCaretColor(int color) { InsetDrawable insetDrawable = (InsetDrawable) edit.getTextCursorDrawable(); insetDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); edit.setTextCursorDrawable(insetDrawable); - Log.d("[UMI]", String.format("set caret cursor: %s", color)); + if (Plugin.bridge.isDebug) { + Log.d("[UMI]", String.format("set caret cursor: %s", color)); + } } if (edit.getTextSelectHandle() instanceof BitmapDrawable) { BitmapDrawable insetDrawable = (BitmapDrawable) edit.getTextSelectHandle(); insetDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); edit.setTextSelectHandle(insetDrawable); - Log.d("[UMI]", String.format("set caret handle: %s", color)); + if (Plugin.bridge.isDebug) { + Log.d("[UMI]", String.format("set caret handle: %s", color)); + } } if (edit.getTextSelectHandleRight() instanceof BitmapDrawable) { BitmapDrawable insetDrawable = (BitmapDrawable) edit.getTextSelectHandleRight(); insetDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); edit.setTextSelectHandleRight(insetDrawable); - Log.d("[UMI]", String.format("set caret handle right: %s", color)); + if (Plugin.bridge.isDebug) { + Log.d("[UMI]", String.format("set caret handle right: %s", color)); + } } if (edit.getTextSelectHandleLeft() instanceof BitmapDrawable) { BitmapDrawable insetDrawable = (BitmapDrawable) edit.getTextSelectHandleLeft(); insetDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); edit.setTextSelectHandleLeft(insetDrawable); - Log.d("[UMI]", String.format("set caret handle left: %s", color)); + if (Plugin.bridge.isDebug) { + Log.d("[UMI]", String.format("set caret handle left: %s", color)); + } } } else { try { @@ -574,6 +597,49 @@ void setCaretColor(int color) { } } + /** + * Update locale list + * + * @param languageCode New language code + * @return Updated LocaleList + */ + private LocaleList UpdateLocaleList(String languageCode) { + LocaleList locales = edit.getImeHintLocales(); + if (locales == null) { + return new LocaleList(new Locale(languageCode)); + } + ArrayList list = new ArrayList<>(Arrays.asList(locales.toLanguageTags().split(","))); + if (!list.contains(languageCode)) { + list.add(languageCode); + } + ArrayList updated = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + updated.add(new Locale(list.get(i))); + } + return new LocaleList(updated.toArray(new Locale[updated.size()])); + } + + /** + * Set keyboard language for input + * + * @param languageCode Language ISO code + */ + private void setKeyboardLanguage(String languageCode) { + if (Plugin.bridge.isDebug) { + Log.d("[UMI]", String.format("set keyboard language: %s", languageCode)); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + edit.setImeHintLocales(UpdateLocaleList(languageCode)); + } else { + Locale locale = new Locale(languageCode); + Configuration config = new Configuration(); + config.locale = locale; + Plugin.activity.getResources().updateConfiguration(config, null); + } + InputMethodManager imm = (InputMethodManager) Plugin.activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.restartInput(edit); + } + /** * Remove MobileInput */ diff --git a/CHANGELOG.md b/CHANGELOG.md index 9644f33..9fa559a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [2.0.4] - 2024-09-10 +- ### Added +- Change keyboard language + ## [2.0.3] - 2024-08-28 - ### Added - Custom text selection color (Android) diff --git a/Documentation~/index.md b/Documentation~/index.md index 9e8e9c7..c72ad68 100644 --- a/Documentation~/index.md +++ b/Documentation~/index.md @@ -99,6 +99,8 @@ This is the basic script for using UMI. Add this script to a game object with `T `SetVisible(bool isVisible)` – switch field visibility +`SetLanguage(string value)` – change keyboard language + `SetRect(RectTransform inputRect)` – set new field size and position, this is useful if you want to move or resize the input field manually, at other times this is done automatically using the game object parameters `SetContentType(InputContentType type)` – set content type to field diff --git a/Editor/MobileInputEditor.cs b/Editor/MobileInputEditor.cs index f0bdd59..bd5b8ac 100644 --- a/Editor/MobileInputEditor.cs +++ b/Editor/MobileInputEditor.cs @@ -68,6 +68,10 @@ public override void OnInspectorGUI() { GUILayout.Label("Custom font name:", GUILayout.MaxWidth(LABEL_SIZE)); _target.CustomFont = GUILayout.TextField(_target.CustomFont); GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(); + GUILayout.Label("Keyboard language:", GUILayout.MaxWidth(LABEL_SIZE)); + _target.KeyboardLanguage = GUILayout.TextField(_target.KeyboardLanguage); + GUILayout.EndHorizontal(); GUILayout.Space(SPACE); _target.IsManualHideControl = GUILayout.Toggle(_target.IsManualHideControl, " Manual hide control"); GUILayout.Space(SPACE); diff --git a/Plugins/Android/Mobileinput.aar b/Plugins/Android/Mobileinput.aar index f2d7b82..4211e99 100644 Binary files a/Plugins/Android/Mobileinput.aar and b/Plugins/Android/Mobileinput.aar differ diff --git a/Plugins/iOS/MobileInput.mm b/Plugins/iOS/MobileInput.mm index 9624c2d..91633d9 100644 --- a/Plugins/iOS/MobileInput.mm +++ b/Plugins/iOS/MobileInput.mm @@ -13,6 +13,7 @@ #define SET_PTEXT_COLOR @"SET_PTEXT_COLOR" #define SET_BG_COLOR @"SET_BG_COLOR" #define SET_READ_ONLY @"SET_READ_ONLY" +#define SET_LANGUAGE @"SET_LANGUAGE" #define SET_RECT @"SET_RECT" #define ON_FOCUS @"ON_FOCUS" #define ON_UNFOCUS @"ON_UNFOCUS" @@ -31,6 +32,13 @@ /// Dict with inputs NSMutableDictionary *mobileInputList = nil; +/// Custom textfield with overridden input mode +@interface CustomTextField : UITextField + +/// Language code +@property (nonatomic, strong) NSString *languageCode; +@end + /// Interface for placeholder @interface PlaceholderTextView : UITextView @@ -42,6 +50,9 @@ @interface PlaceholderTextView : UITextView /// Placeholder text color @property(nonatomic, strong) UIColor *placeholderColor UI_APPEARANCE_SELECTOR; + +/// Language code +@property (nonatomic, strong) NSString *languageCode; @end /// MobileInput interface @@ -107,6 +118,35 @@ - (void)showKeyboard:(BOOL)value; - (BOOL)isFocused; @end +/// Custom textfield implemenation +@implementation CustomTextField + +/// Set language for keyboard +/// - Parameter languageCode: ISO code +- (void)setLanguageCode:(NSString *)languageCode { + _languageCode = languageCode; + if ([self isFirstResponder]) { + [self resignFirstResponder]; + [self becomeFirstResponder]; + } +} + +/// Overridden input +- (UITextInputMode *)textInputMode { + if (self.languageCode) { + for (UITextInputMode *keyboard in UITextInputMode.activeInputModes) { + if (keyboard.primaryLanguage) { + NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:keyboard.primaryLanguage]; + if ([locale.languageCode isEqualToString:self.languageCode]) { + return keyboard; + } + } + } + } + return [super textInputMode]; +} + +@end /// Placeholder interface @interface PlaceholderTextView () @@ -240,10 +280,35 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } +/// Set language for keyboard +/// - Parameter languageCode: ISO code +- (void)setLanguageCode:(NSString *)languageCode { + _languageCode = languageCode; + if ([self isFirstResponder]) { + [self resignFirstResponder]; + [self becomeFirstResponder]; + } +} + +/// Overidden input +- (UITextInputMode *)textInputMode { + if (self.languageCode) { + for (UITextInputMode *keyboard in UITextInputMode.activeInputModes) { + if (keyboard.primaryLanguage) { + NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:keyboard.primaryLanguage]; + if ([locale.languageCode isEqualToString:self.languageCode]) { + return keyboard; + } + } + } + } + return [super textInputMode]; +} + @end -/// MobileInput implimenation +/// MobileInput implemenation @implementation MobileInput /// Init MobileInput @@ -434,6 +499,13 @@ - (void)processData:(NSDictionary *)data { } else if ([msg isEqualToString:SET_VISIBLE]) { BOOL isVisible = [[data valueForKey:@"is_visible"] boolValue]; [self setVisible:isVisible]; + } else if ([msg isEqualToString:SET_LANGUAGE]) { + NSString *code = [data valueForKey:@"value"]; + if (isMultiline) { + [(PlaceholderTextView *)editView setLanguageCode:code]; + } else { + [(CustomTextField *)editView setLanguageCode:code]; + } } } @@ -512,7 +584,7 @@ - (void)create:(NSDictionary *)data { float caretColor_r = [[data valueForKey:@"caret_color_r"] floatValue]; float caretColor_g = [[data valueForKey:@"caret_color_g"] floatValue]; float caretColor_b = [[data valueForKey:@"caret_color_b"] floatValue]; - float caretColor_a = [[data valueForKey:@"caret_color_a"] floatValue]; + float caretColor_a = [[data valueForKey:@"caret_color_a"] floatValue]; UIColor *caretColor = [UIColor colorWithRed:caretColor_r green:caretColor_g blue:caretColor_b alpha:caretColor_a]; NSString *contentType = [data valueForKey:@"content_type"]; NSString *alignment = [data valueForKey:@"align"]; @@ -525,6 +597,7 @@ - (void)create:(NSDictionary *)data { BOOL password = NO; NSString *inputType = [data valueForKey:@"input_type"]; NSString *keyboardType = [data valueForKey:@"keyboard_type"]; + NSString *keyboardLanguage = [data valueForKey:@"keyboard_language"]; UIKeyboardType keyType = UIKeyboardTypeDefault; if ([contentType isEqualToString:@"Autocorrected"]) { autoCorrection = YES; @@ -665,6 +738,9 @@ - (void)create:(NSDictionary *)data { if (isChangeCaret) { textView.tintColor = caretColor; } + if (![keyboardLanguage isEqualToString:@"default"]) { + textView.languageCode = keyboardLanguage; + } textView.delegate = self; if (keyType == UIKeyboardTypeEmailAddress) { textView.autocapitalizationType = UITextAutocapitalizationTypeNone; @@ -675,7 +751,7 @@ - (void)create:(NSDictionary *)data { } editView = textView; } else { - UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(x, y, width, height)]; + CustomTextField *textField = [[CustomTextField alloc] initWithFrame:CGRectMake(x, y, width, height)]; textField.keyboardType = keyType; [textField setFont:uiFont]; textField.delegate = self; @@ -686,6 +762,9 @@ - (void)create:(NSDictionary *)data { if (isChangeCaret) { textField.tintColor = caretColor; } + if (![keyboardLanguage isEqualToString:@"default"]) { + textField.languageCode = keyboardLanguage; + } textField.returnKeyType = returnKeyType; textField.autocorrectionType = autoCorrection ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo; textField.contentVerticalAlignment = valign; diff --git a/README.md b/README.md index 1f35a4f..c0dcde0 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ From UMI, you can edit these additional options: - return button type - return button callback - custom font +- keyboard language on init - manual hide option - done & clear buttons option @@ -139,6 +140,7 @@ With `OnKeyboardAction` you can control UI elements, such as moving the input fi - `SetBackgroundColor` – change background color - `SetContentType` – change input field content type - `SetReadonly` – change readonly mode +- `SetLanguage` – change keyboard language ### How to use custom fonts diff --git a/README.ru.md b/README.ru.md index 8cd0d40..7802bf2 100644 --- a/README.ru.md +++ b/README.ru.md @@ -99,6 +99,7 @@ UMI также позволяет дополнительно настраива - тип кнопки - обработку кнопки - свой шрифт +- язык клавиатуры при инициализации - управление скрытием клавиатуры - кнопки "Готово" и "Очистить" @@ -138,6 +139,7 @@ public class Bootstrap : MonoBehaviour { - `SetBackgroundColor` – изменение цвета фона - `SetContentType` – изменение типа поля ввода - `SetReadonly` – изменение состояния "только для чтения" +- `SetLanguage` – изменение языка клавиатуры ### Как использовать свои шрифты diff --git a/Runtime/MobileInput.cs b/Runtime/MobileInput.cs index 76b2a2d..c120747 100644 --- a/Runtime/MobileInput.cs +++ b/Runtime/MobileInput.cs @@ -456,7 +456,7 @@ public static float GetScreenScale() { #else return 1f; #endif - } + } /// /// Update fonts diff --git a/Runtime/MobileInputField.cs b/Runtime/MobileInputField.cs index 2e9e402..e1ee1d6 100644 --- a/Runtime/MobileInputField.cs +++ b/Runtime/MobileInputField.cs @@ -133,6 +133,11 @@ public enum ReturnKeyType { /// const string SET_VISIBLE = "SET_VISIBLE"; + /// + /// Set language to keyboard + /// + const string SET_LANGUAGE = "SET_LANGUAGE"; + /// /// Event when text changing in InputField /// @@ -165,6 +170,11 @@ public enum ReturnKeyType { /// public string CustomFont = "default"; + /// + /// Custom keyboard language, ISO code + /// + public string KeyboardLanguage = "default"; + /// /// Background color /// @@ -593,6 +603,7 @@ void CreateNativeEdit() { data["multiline"] = _config.Multiline; data["input_type"] = _config.InputType; data["keyboard_type"] = _config.KeyboardType; + data["keyboard_language"] = KeyboardLanguage; data["return_key_type"] = ReturnKey switch { ReturnKeyType.Next => (JsonNode)"Next", ReturnKeyType.Done => (JsonNode)"Done", @@ -681,6 +692,17 @@ public void SetReadonly(bool value) { Execute(data); } + /// + /// Set language to keyboard + /// + /// ISO language code + public void SetLanguage(string value) { + var data = new JsonObject(); + data["msg"] = SET_LANGUAGE; + data["value"] = value; + Execute(data); + } + /// /// Set text to field ///