diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java index f51267e2a..ebea9f567 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -12,9 +12,12 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.utils.LocaleUtils; +import org.mozilla.vrbrowser.utils.StringUtils; import androidx.annotation.NonNull; +import java.util.Locale; + import static org.mozilla.vrbrowser.utils.ServoUtils.isServoAvailable; public class SettingsStore { @@ -423,5 +426,19 @@ public void setFoveatedLevelWebVR(int level) { editor.putInt(mContext.getString(R.string.settings_key_foveated_webvr), level); editor.commit(); } + + public void setSelectedKeyboard(Locale aLocale) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putString(mContext.getString(R.string.settings_key_keyboard_locale), aLocale.toLanguageTag()); + editor.commit(); + } + + public Locale getKeyboardLocale() { + String value = mPrefs.getString(mContext.getString(R.string.settings_key_keyboard_locale), null); + if (StringUtils.isEmpty(value)) { + return null; + } + return Locale.forLanguageTag(value); + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java index 3346b8ead..d04209655 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java @@ -180,7 +180,7 @@ public boolean usesComposingText() { @Override public String getKeyboardTitle() { - return mContext.getString(R.string.settings_language_simplified_chinese); + return StringUtils.getStringByLocale(mContext, R.string.settings_language_simplified_chinese, getLocale()); } @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/EnglishKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/EnglishKeyboard.java index 18a671bf0..d5b6a6953 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/EnglishKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/EnglishKeyboard.java @@ -4,6 +4,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.input.CustomKeyboard; +import org.mozilla.vrbrowser.utils.StringUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -34,7 +35,7 @@ public CandidatesResult getCandidates(String aText) { @Override public String getKeyboardTitle() { - return mContext.getString(R.string.settings_language_english); + return StringUtils.getStringByLocale(mContext, R.string.settings_language_english, getLocale()); } @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/SpanishKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/SpanishKeyboard.java new file mode 100644 index 000000000..31c1a7c74 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/SpanishKeyboard.java @@ -0,0 +1,46 @@ +package org.mozilla.vrbrowser.ui.keyboards; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.input.CustomKeyboard; +import org.mozilla.vrbrowser.utils.StringUtils; + +import java.util.Locale; + +public class SpanishKeyboard extends BaseKeyboard { + private CustomKeyboard mKeyboard; + private final Locale mSpanishLocale = new Locale("es", ""); + + public SpanishKeyboard(Context aContext) { + super(aContext); + } + + @NonNull + @Override + public CustomKeyboard getAlphabeticKeyboard() { + if (mKeyboard == null) { + mKeyboard = new CustomKeyboard(mContext.getApplicationContext(), R.xml.keyboard_qwerty_spanish); + } + return mKeyboard; + } + + @Nullable + @Override + public CandidatesResult getCandidates(String aText) { + return null; + } + + @Override + public String getKeyboardTitle() { + return StringUtils.getStringByLocale(mContext, R.string.settings_language_spanish, getLocale()); + } + + @Override + public Locale getLocale() { + return mSpanishLocale; + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomKeyboardView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomKeyboardView.java index 3bf0f891f..b4cc9a71d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomKeyboardView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomKeyboardView.java @@ -672,6 +672,7 @@ public void onSizeChanged(int w, int h, int oldw, int oldh) { } if (mResizeMethod != null) { try { + mResizeMethod.setAccessible(true); mResizeMethod.invoke(mKeyboard, w, h); } catch (Exception ex) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LanguageSelectorView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LanguageSelectorView.java new file mode 100644 index 000000000..d882dc4b2 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LanguageSelectorView.java @@ -0,0 +1,103 @@ +package org.mozilla.vrbrowser.ui.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.FrameLayout; +import android.widget.GridLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +import java.util.List; + + +public class LanguageSelectorView extends FrameLayout { + public static class Item { + public final String title; + public final Object tag; + + public Item(String aTitle, Object aTag) { + this.title = aTitle; + this.tag = aTag; + } + } + + public interface Delegate { + void onLanguageClick(Item aItem); + } + + private GridLayout mLangRowContainer; + private Delegate mDelegate; + private List mItems; + private int mItemWidth; + private static final int kMaxItemsPerColumn = 4; + + public LanguageSelectorView(@NonNull Context context) { + super(context); + initialize(); + } + + public LanguageSelectorView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initialize(); + } + + public LanguageSelectorView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(); + } + + private void initialize() { + inflate(getContext(), R.layout.language_selection, this); + mLangRowContainer = findViewById(R.id.langRowContainer); + mItemWidth = WidgetPlacement.pixelDimension(getContext(), R.dimen.lang_selector_item_width); + } + + public void setDelegate(Delegate aDelegate) { + mDelegate = aDelegate; + } + + public void setItems(List aItems) { + mItems = aItems; + mLangRowContainer.removeAllViews(); + int columns = (aItems.size() / kMaxItemsPerColumn) + 1; + int rows = aItems.size() < kMaxItemsPerColumn ? aItems.size() : kMaxItemsPerColumn; + mLangRowContainer.setColumnCount(columns); + mLangRowContainer.setRowCount(rows); + + for (Item item: aItems) { + GridLayout.LayoutParams params = new GridLayout.LayoutParams(); + params.width = mItemWidth; + mLangRowContainer.addView(createLangButton(item), params); + } + } + + public List getItems() { + return mItems; + } + + private OnClickListener clickHandler = v -> { + UITextButton button = (UITextButton) v; + if (mDelegate != null) { + mDelegate.onLanguageClick((Item)button.getTag()); + } + }; + + private UITextButton createLangButton(Item aItem) { + UITextButton button = new UITextButton(getContext()); + button.setTintColorList(R.drawable.lang_selector_button_color); + button.setBackground(getContext().getDrawable(R.drawable.lang_selector_button_background)); + button.setOnClickListener(clickHandler); + button.setPadding(13, 13, 13, 13); + button.setIncludeFontPadding(false); + button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13); + button.setText(aItem.title); + button.setTextAlignment(TEXT_ALIGNMENT_VIEW_START); + button.setTag(aItem); + + return button; + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java index 7b5c76b3e..98c0eb0ac 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java @@ -10,6 +10,7 @@ import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard; import android.os.Handler; +import android.os.LocaleList; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; @@ -29,20 +30,25 @@ import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.browser.SessionStore; +import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.input.CustomKeyboard; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.ui.keyboards.KeyboardInterface; +import org.mozilla.vrbrowser.ui.keyboards.SpanishKeyboard; import org.mozilla.vrbrowser.ui.views.AutoCompletionView; import org.mozilla.vrbrowser.ui.views.CustomKeyboardView; +import org.mozilla.vrbrowser.ui.views.LanguageSelectorView; import org.mozilla.vrbrowser.ui.widgets.dialogs.VoiceSearchWidget; import org.mozilla.vrbrowser.ui.keyboards.ChinesePinyinKeyboard; import org.mozilla.vrbrowser.ui.keyboards.EnglishKeyboard; import org.mozilla.vrbrowser.utils.StringUtils; import java.util.ArrayList; +import java.util.Locale; import java.util.regex.Pattern; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKeyboardActionListener, AutoCompletionView.Delegate, @@ -69,6 +75,7 @@ public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKey private EditorInfo mEditorInfo = new EditorInfo(); private VoiceSearchWidget mVoiceSearchWidget; private AutoCompletionView mAutoCompletionView; + private LanguageSelectorView mLanguageSelectorView; private int mKeyWidth; private int mKeyboardPopupTopMargin; @@ -107,11 +114,14 @@ private void initialize(Context aContext) { mKeyboardNumericView = findViewById(R.id.keyboardNumeric); mPopupKeyboardview = findViewById(R.id.popupKeyboard); mPopupKeyboardLayer = findViewById(R.id.popupKeyboardLayer); + mLanguageSelectorView = findViewById(R.id.langSelectorView); + mLanguageSelectorView.setDelegate(aItem -> handleLanguageChange((KeyboardInterface) aItem.tag)); mKeyboards = new ArrayList<>(); mKeyboards.add(new EnglishKeyboard(aContext)); + mKeyboards.add(new SpanishKeyboard(aContext)); mKeyboards.add(new ChinesePinyinKeyboard(aContext)); - mCurrentKeyboard = mKeyboards.get(0); + setDefaultKeyboard(); mKeyboardSymbols = new CustomKeyboard(aContext.getApplicationContext(), R.xml.keyboard_symbols); mKeyboardNumeric = new CustomKeyboard(aContext.getApplicationContext(), R.xml.keyboard_numeric); @@ -131,11 +141,6 @@ private void initialize(Context aContext) { mKeyboardNumericView.setPreviewEnabled(false); - setOnClickListener(view -> { - mPopupKeyboardview.setVisibility(View.GONE); - mPopupKeyboardLayer.setVisibility(View.GONE); - }); - int[] featuredKeys = { ' ', Keyboard.KEYCODE_DELETE, Keyboard.KEYCODE_DONE, Keyboard.KEYCODE_CANCEL, Keyboard.KEYCODE_MODE_CHANGE, CustomKeyboard.KEYCODE_VOICE_INPUT, CustomKeyboard.KEYCODE_SYMBOLS_CHANGE, CustomKeyboard.KEYCODE_LANGUAGE_CHANGE, @@ -158,10 +163,8 @@ private void initialize(Context aContext) { mKeyWidth = getResources().getDimensionPixelSize(R.dimen.keyboard_key_width); mKeyboardPopupTopMargin = getResources().getDimensionPixelSize(R.dimen.keyboard_key_pressed_padding) * 2; - mPopupKeyboardLayer.setOnClickListener(view -> { - mPopupKeyboardview.setVisibility(View.GONE); - mPopupKeyboardLayer.setVisibility(View.GONE); - }); + setOnClickListener(view -> hideOverlays()); + mPopupKeyboardLayer.setOnClickListener(view -> hideOverlays()); mKeyboardView.setVisibility(View.VISIBLE); mKeyboardNumericView.setKeyboard(mKeyboardNumeric); @@ -247,9 +250,10 @@ public void updateFocusedView(View aFocusedView) { final InputConnection input = mInputConnection; postInputCommand(() -> { displayComposingText(""); - mInputConnection.finishComposingText(); + input.finishComposingText(); }); } + hideOverlays(); mInputConnection = null; } @@ -282,9 +286,13 @@ public void dismiss() { mIsCapsLock = false; mIsLongPress = false; handleShift(false); + hideOverlays(); + } - mPopupKeyboardview.setVisibility(View.GONE); - mPopupKeyboardLayer.setVisibility(View.GONE); + private void hideOverlays() { + mPopupKeyboardview.setVisibility(View.GONE); + mPopupKeyboardLayer.setVisibility(View.GONE); + mLanguageSelectorView.setVisibility(View.GONE); } protected void onDismiss() { @@ -393,7 +401,7 @@ public void onKey(int primaryCode, int[] keyCodes, boolean hasPopup) { handleVoiceInput(); break; case CustomKeyboard.KEYCODE_LANGUAGE_CHANGE: - handleChangeLanguage(); + handleGlobeClick(); break; case ' ': handleSpace(); @@ -426,7 +434,7 @@ public void onNoKey() { @Override public void onText(CharSequence text) { - + handleText(text.toString()); } @Override @@ -449,6 +457,46 @@ public void swipeUp() { } + private void setDefaultKeyboard() { + mCurrentKeyboard = getKeyboardForLocale(SettingsStore.getInstance(getContext()).getKeyboardLocale()); + if (mCurrentKeyboard != null) { + return; + } + + // If the user has not selected any keyboard, find the best match from system locales. + LocaleList localeList = getResources().getConfiguration().getLocales(); + String[] supportedLocales = new String[mKeyboards.size()]; + for (int i = 0; i < mKeyboards.size(); ++i) { + supportedLocales[i] = mKeyboards.get(i).getLocale().toLanguageTag(); + } + Locale bestMatch = localeList.getFirstMatch(supportedLocales); + mCurrentKeyboard = getKeyboardForLocale(bestMatch); + if (mCurrentKeyboard == null) { + // Fall back to english. + mCurrentKeyboard = getKeyboardForLocale(Locale.ENGLISH); + } + } + + private KeyboardInterface getKeyboardForLocale(@Nullable Locale aLocale) { + if (aLocale == null) { + return null; + } + // Check perfect locale mach + for (KeyboardInterface keyboard: mKeyboards) { + if (keyboard.getLocale().equals(aLocale)) { + return keyboard; + } + } + // Fall back to language check + for (KeyboardInterface keyboard: mKeyboards) { + if (keyboard.getLocale().getLanguage().equalsIgnoreCase(aLocale.getLanguage())) { + return keyboard; + } + } + return null; + } + + private void handleShift(boolean isShifted) { CustomKeyboard keyboard = (CustomKeyboard) mKeyboardView.getKeyboard(); boolean shifted = isShifted; @@ -508,14 +556,23 @@ private void handleBackspace(final boolean isLongPress) { } - private void handleChangeLanguage() { - int index = mKeyboards.indexOf(mCurrentKeyboard); - if (index < 0) { - index = 0; + private void handleGlobeClick() { + if (mLanguageSelectorView.getItems() == null || mLanguageSelectorView.getItems().size() == 0) { + ArrayList items = new ArrayList<>(); + for (KeyboardInterface keyboard: mKeyboards) { + items.add(new LanguageSelectorView.Item(keyboard.getKeyboardTitle().toUpperCase(), keyboard)); + } + mLanguageSelectorView.setItems(items); } - index = (index + 1) % mKeyboards.size(); - mCurrentKeyboard = mKeyboards.get(index); + mLanguageSelectorView.setVisibility(View.VISIBLE); + mPopupKeyboardLayer.setVisibility(View.VISIBLE); + } + + private void handleLanguageChange(KeyboardInterface aKeyboard) { + mCurrentKeyboard = aKeyboard; + SettingsStore.getInstance(getContext()).setSelectedKeyboard(aKeyboard.getLocale()); mKeyboardView.setKeyboard(mCurrentKeyboard.getAlphabeticKeyboard()); + hideOverlays(); updateCandidates(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/StringUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/StringUtils.java index 56e5239c6..f94225bbe 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/StringUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/StringUtils.java @@ -21,4 +21,8 @@ public static String getStringByLocale(Context context, int id, Locale locale) { public static String removeSpaces(@NonNull String aText) { return aText.replaceAll("\\s", ""); } + + public static boolean isEmpty(String aString) { + return aString == null || aString.length() == 0; + } } diff --git a/app/src/main/res/drawable/lang_selector_background.xml b/app/src/main/res/drawable/lang_selector_background.xml new file mode 100644 index 000000000..344d3900c --- /dev/null +++ b/app/src/main/res/drawable/lang_selector_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/lang_selector_button_background.xml b/app/src/main/res/drawable/lang_selector_button_background.xml new file mode 100644 index 000000000..79999b0b9 --- /dev/null +++ b/app/src/main/res/drawable/lang_selector_button_background.xml @@ -0,0 +1,21 @@ + + + + + + + + > + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/lang_selector_button_color.xml b/app/src/main/res/drawable/lang_selector_button_color.xml new file mode 100644 index 000000000..4ce497ea7 --- /dev/null +++ b/app/src/main/res/drawable/lang_selector_button_color.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/keyboard.xml b/app/src/main/res/layout/keyboard.xml index 931fc3cec..8bb318cd1 100644 --- a/app/src/main/res/layout/keyboard.xml +++ b/app/src/main/res/layout/keyboard.xml @@ -47,6 +47,18 @@ android:shadowRadius="0.0" android:verticalCorrection="0dp" android:visibility="visible"/> + + + + + + + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 646f89e5b..5146b6025 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -59,6 +59,9 @@ 100dp 125dp + + 120dp + 88dp 14dp diff --git a/app/src/main/res/values/non_L10n.xml b/app/src/main/res/values/non_L10n.xml index fd2e1b726..f60a94aab 100644 --- a/app/src/main/res/values/non_L10n.xml +++ b/app/src/main/res/values/non_L10n.xml @@ -30,6 +30,7 @@ settings_cylinder_density settings_foveated_app settings_foveated_webvr + settings_key_keyboard_locale https://support.mozilla.org/kb/private-mode-firefox-reality settings_browser_world_width settings_browser_world_height diff --git a/app/src/main/res/xml/keyboard_qwerty_spanish.xml b/app/src/main/res/xml/keyboard_qwerty_spanish.xml new file mode 100644 index 000000000..71891d9a7 --- /dev/null +++ b/app/src/main/res/xml/keyboard_qwerty_spanish.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file