diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 97bad787c..93bb05765 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,7 +18,7 @@ android { abiFilters.clear() abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")) } - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } buildTypes { diff --git a/app/src/main/assets/emoji/SYMBOLS.txt b/app/src/main/assets/emoji/SYMBOLS.txt index 02effebc7..f85cc13bb 100644 --- a/app/src/main/assets/emoji/SYMBOLS.txt +++ b/app/src/main/assets/emoji/SYMBOLS.txt @@ -136,6 +136,32 @@ ®️ ™️ 🫟 +🇦 +🇧 +🇨 +🇩 +🇪 +🇫 +🇬 +🇭 +🇮 +🇯 +🇰 +🇱 +🇲 +🇳 +🇴 +🇵 +🇶 +🇷 +🇸 +🇹 +🇺 +🇻 +🇼 +🇽 +🇾 +🇿 #️⃣ *️⃣ 0️⃣ @@ -221,4 +247,4 @@ 💠 🔘 🔳 -🔲 +🔲 \ No newline at end of file diff --git a/app/src/main/java/helium314/keyboard/event/Event.kt b/app/src/main/java/helium314/keyboard/event/Event.kt index 37dc1ea79..75fda0535 100644 --- a/app/src/main/java/helium314/keyboard/event/Event.kt +++ b/app/src/main/java/helium314/keyboard/event/Event.kt @@ -139,16 +139,6 @@ class Event private constructor( null, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, null) } - // A helper method to split the code point and the key code. - // todo: Ultimately, they should not be squashed into the same variable, and this method should be removed. - @JvmStatic - fun createSoftwareKeypressEvent(keyCodeOrCodePoint: Int, metaState: Int, keyX: Int, keyY: Int, isKeyRepeat: Boolean) = - if (keyCodeOrCodePoint <= 0) { - createSoftwareKeypressEvent(NOT_A_CODE_POINT, keyCodeOrCodePoint, metaState, keyX, keyY, isKeyRepeat) - } else { - createSoftwareKeypressEvent(keyCodeOrCodePoint, NOT_A_KEY_CODE, metaState, keyX, keyY, isKeyRepeat) - } - fun createHardwareKeypressEvent(codePoint: Int, keyCode: Int, metaState: Int, next: Event?, isKeyRepeat: Boolean): Event { return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState, Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE, @@ -266,8 +256,10 @@ class Event private constructor( source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, source.mNextEvent) } - val notHandledEvent = Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0, + fun createNotHandledEvent(): Event { + return Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null) + } } // This method is private - to create a new event, use one of the create* utility methods. diff --git a/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt b/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt index a8d2d7f32..2742ee193 100644 --- a/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt +++ b/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt @@ -24,8 +24,7 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder { // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value // that includes both the unicode char in the lower 21 bits and flags in the upper bits, // hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info. - val codePointAndFlags = keyEvent.unicodeChar.takeIf { it != 0 } - ?: Event.NOT_A_CODE_POINT // KeyEvent has 0 if no codePoint, but that's actually valid so we convert it to -1 + val codePointAndFlags = keyEvent.unicodeChar // The keyCode is the abstraction used by the KeyEvent to represent different keys that // do not necessarily map to a unicode character. This represents a physical key, like // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock. @@ -49,21 +48,6 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder { } else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat) // If not Enter, then this is just a regular keypress event for a normal character // that can be committed right away, taking into account the current state. - } else if (isDpadDirection(keyCode)) { - Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat) -// } else if (KeyEvent.isModifierKey(keyCode)) { -// todo: we could synchronize meta state across HW and SW keyboard, but that's more work for little benefit (especially with shift & caps lock) - } else { - Event.notHandledEvent - } - } - - companion object { - private fun isDpadDirection(keyCode: Int) = when (keyCode) { - KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.KEYCODE_DPAD_DOWN_LEFT, KeyEvent.KEYCODE_DPAD_DOWN_RIGHT, KeyEvent.KEYCODE_DPAD_UP_RIGHT, - KeyEvent.KEYCODE_DPAD_UP_LEFT -> true - else -> false - } + } else Event.createNotHandledEvent() } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java index 20fb20839..fb177e806 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java @@ -6,8 +6,6 @@ package helium314.keyboard.keyboard; -import android.view.KeyEvent; - import helium314.keyboard.latin.common.Constants; import helium314.keyboard.latin.common.InputPointers; @@ -33,12 +31,6 @@ public interface KeyboardActionListener { */ void onReleaseKey(int primaryCode, boolean withSliding); - /** For handling hardware key presses. Returns whether the event was handled. */ - boolean onKeyDown(int keyCode, KeyEvent keyEvent); - - /** For handling hardware key presses. Returns whether the event was handled. */ - boolean onKeyUp(int keyCode, KeyEvent keyEvent); - /** * Send a key code to the listener. * @@ -125,10 +117,6 @@ public interface KeyboardActionListener { @Override public void onReleaseKey(int primaryCode, boolean withSliding) {} @Override - public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { return false; } - @Override - public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { return false; } - @Override public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat) {} @Override public void onTextInput(String text) {} diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt index f75e2825e..fc002a187 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt @@ -1,24 +1,16 @@ package helium314.keyboard.keyboard import android.text.InputType -import android.util.SparseArray import android.view.KeyEvent import android.view.inputmethod.InputMethodSubtype -import helium314.keyboard.event.Event -import helium314.keyboard.event.HangulEventDecoder -import helium314.keyboard.event.HardwareEventDecoder -import helium314.keyboard.event.HardwareKeyboardEventDecoder import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.latin.EmojiAltPhysicalKeyDetector import helium314.keyboard.latin.LatinIME import helium314.keyboard.latin.RichInputMethodManager import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.common.InputPointers import helium314.keyboard.latin.common.StringUtils -import helium314.keyboard.latin.common.combiningRange import helium314.keyboard.latin.common.loopOverCodePoints import helium314.keyboard.latin.common.loopOverCodePointsBackwards -import helium314.keyboard.latin.define.ProductionFlags import helium314.keyboard.latin.inputlogic.InputLogic import helium314.keyboard.latin.settings.Settings import kotlin.math.abs @@ -27,11 +19,6 @@ import kotlin.math.min class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inputLogic: InputLogic) : KeyboardActionListener { private val connection = inputLogic.mConnection - private val emojiAltPhysicalKeyDetector by lazy { EmojiAltPhysicalKeyDetector(latinIME.resources) } - - // We expect to have only one decoder in almost all cases, hence the default capacity of 1. - // If it turns out we need several, it will get grown seamlessly. - private val hardwareEventDecoders: SparseArray = SparseArray(1) private val keyboardSwitcher = KeyboardSwitcher.getInstance() private val settings = Settings.getInstance() @@ -71,62 +58,9 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp keyboardSwitcher.onReleaseKey(primaryCode, withSliding, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState) } - override fun onKeyUp(keyCode: Int, keyEvent: KeyEvent): Boolean { - emojiAltPhysicalKeyDetector.onKeyUp(keyEvent) - if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) - return false - - val keyIdentifier = keyEvent.deviceId.toLong() shl 32 + keyEvent.keyCode - return inputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier) - } - - override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean { - emojiAltPhysicalKeyDetector.onKeyDown(keyEvent) - if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) - return false - - val event: Event - if (settings.current.mLocale.language == "ko") { // todo: this does not appear to be the right place - val subtype = keyboardSwitcher.keyboard?.mId?.mSubtype ?: RichInputMethodManager.getInstance().currentSubtype - event = HangulEventDecoder.decodeHardwareKeyEvent(subtype, keyEvent) { - getHardwareKeyEventDecoder(keyEvent.deviceId).decodeHardwareKey(keyEvent) - } - } else { - event = getHardwareKeyEventDecoder(keyEvent.deviceId).decodeHardwareKey(keyEvent) - } - - if (event.isHandled) { - inputLogic.onCodeInput( - settings.current, event, - keyboardSwitcher.getKeyboardShiftMode(), // TODO: this is not necessarily correct for a hardware keyboard right now - keyboardSwitcher.getCurrentKeyboardScript(), - latinIME.mHandler - ) - return true - } - return false - } - override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) { - when (primaryCode) { - KeyCode.TOGGLE_AUTOCORRECT -> return Settings.getInstance().toggleAutoCorrect() - KeyCode.TOGGLE_INCOGNITO_MODE -> return Settings.getInstance().toggleAlwaysIncognitoMode() - } val mkv = keyboardSwitcher.mainKeyboardView - - // checking if the character is a combining accent - val event = if (primaryCode in combiningRange) { // todo: should this be done later, maybe in inputLogic? - Event.createSoftwareDeadEvent(primaryCode, 0, metaState, mkv.getKeyX(x), mkv.getKeyY(y), null) - } else { - // todo: - // setting meta shift should only be done for arrow and similar cursor movement keys - // should only be enabled once it works more reliably (currently depends on app for some reason) -// if (mkv.keyboard?.mId?.isAlphabetShiftedManually == true) -// Event.createSoftwareKeypressEvent(primaryCode, metaState or KeyEvent.META_SHIFT_ON, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat) -// else Event.createSoftwareKeypressEvent(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat) - Event.createSoftwareKeypressEvent(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat) - } - latinIME.onEvent(event) + latinIME.onCodeInput(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat) } override fun onTextInput(text: String?) = latinIME.onTextInput(text) @@ -323,13 +257,4 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp } return -min(-actualSteps, text.length) } - - private fun getHardwareKeyEventDecoder(deviceId: Int): HardwareEventDecoder { - hardwareEventDecoders.get(deviceId)?.let { return it } - - // TODO: create the decoder according to the specification - val newDecoder = HardwareKeyboardEventDecoder(deviceId) - hardwareEventDecoders.put(deviceId, newDecoder) - return newDecoder - } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java index 62523f488..ac39680a8 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java @@ -184,11 +184,6 @@ public final class KeyboardId { || mElementId == ELEMENT_ALPHABET_AUTOMATIC_SHIFTED || mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED; } - public boolean isAlphabetShiftedManually() { - return mElementId == ELEMENT_ALPHABET_SHIFT_LOCKED || mElementId == ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED - || mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED; - } - public boolean isNumberLayout() { return mElementId == ELEMENT_NUMBER || mElementId == ELEMENT_NUMPAD || mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS; diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java index 34330ad48..e4849c259 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java @@ -138,7 +138,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { return false; } - private void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues, + public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues, final int currentAutoCapsState, final int currentRecapitalizeState) { final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( mThemeContext, editorInfo); @@ -527,10 +527,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (mCurrentInputView == null) return; mEmojiPalettesView.clearKeyboardCache(); - reloadMainKeyboard(); - } - - public void reloadMainKeyboard() { loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), Settings.getValues(), mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState()); } @@ -601,10 +597,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (mKeyboardView == null || !mKeyboardView.isShown()) { return false; } - final Keyboard keyboard = mKeyboardView.getKeyboard(); - if (keyboard == null) // may happen when using hardware keyboard - return false; - int activeKeyboardId = keyboard.mId.mElementId; + int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId; for (int keyboardId : keyboardIds) { if (activeKeyboardId == keyboardId) { return true; diff --git a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java index 76a01e8e5..3a3fa3e16 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java @@ -360,21 +360,25 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy public void onKeyPressed(@NonNull final Key key, final boolean withPreview) { key.onPressed(); invalidateKey(key); - - final Keyboard keyboard = getKeyboard(); - if (keyboard == null) { - return; - } - mKeyPreviewDrawParams.setVisibleOffset(-keyboard.mVerticalGap); - if (withPreview && !key.noKeyPreview() && mKeyPreviewDrawParams.isPopupEnabled()) { + if (withPreview && !key.noKeyPreview()) { showKeyPreview(key); } } private void showKeyPreview(@NonNull final Key key) { + final Keyboard keyboard = getKeyboard(); + if (keyboard == null) { + return; + } + final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; + if (!previewParams.isPopupEnabled()) { + previewParams.setVisibleOffset(-keyboard.mVerticalGap); + return; + } + locatePreviewPlacerView(); getLocationInWindow(mOriginCoords); - mKeyPreviewChoreographer.placeAndShowKeyPreview(key, getKeyboard().mIconsSet, getKeyDrawParams(), + mKeyPreviewChoreographer.placeAndShowKeyPreview(key, keyboard.mIconsSet, getKeyDrawParams(), KeyboardSwitcher.getInstance().getWrapperView().getWidth(), mOriginCoords, mDrawingPreviewPlacerView); } diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java index 4aa543201..7d1735c08 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboardView.java @@ -12,15 +12,15 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import androidx.annotation.NonNull; import helium314.keyboard.accessibility.AccessibilityUtils; import helium314.keyboard.accessibility.PopupKeysKeyboardAccessibilityDelegate; -import helium314.keyboard.keyboard.emoji.EmojiViewCallback; +import helium314.keyboard.keyboard.emoji.OnKeyEventListener; import helium314.keyboard.keyboard.internal.KeyDrawParams; import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.R; @@ -39,7 +39,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane protected final KeyDetector mKeyDetector; private Controller mController = EMPTY_CONTROLLER; protected KeyboardActionListener mListener; - protected EmojiViewCallback mEmojiViewCallback; + protected OnKeyEventListener mKeyEventListener; private int mOriginX; private int mOriginY; private Key mCurrentKey; @@ -122,7 +122,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane public void showPopupKeysPanel(final View parentView, final Controller controller, final int pointX, final int pointY, final KeyboardActionListener listener) { mListener = listener; - mEmojiViewCallback = null; + mKeyEventListener = null; showPopupKeysPanelInternal(parentView, controller, pointX, pointY); } @@ -131,9 +131,9 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane */ @Override public void showPopupKeysPanel(final View parentView, final Controller controller, - final int pointX, final int pointY, final EmojiViewCallback emojiViewCallback) { + final int pointX, final int pointY, final OnKeyEventListener listener) { mListener = null; - mEmojiViewCallback = emojiViewCallback; + mKeyEventListener = listener; showPopupKeysPanelInternal(parentView, controller, pointX, pointY); } @@ -157,9 +157,6 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane mOriginX = x + container.getPaddingLeft(); mOriginY = y + container.getPaddingTop(); - var center = panelX + getMeasuredWidth() / 2; - // This is needed for cases where there's also a long text popup above this keyboard - controller.setLayoutGravity(center < pointX? Gravity.RIGHT : center > pointX? Gravity.LEFT : Gravity.CENTER_HORIZONTAL); controller.onShowPopupKeysPanel(this); final PopupKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; if (accessibilityDelegate != null @@ -225,8 +222,8 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane false /* isKeyRepeat */); } } - } else if (mEmojiViewCallback != null) { - mEmojiViewCallback.onReleaseKey(key); + } else if (mKeyEventListener != null) { + mKeyEventListener.onReleaseKey(key); } } @@ -317,4 +314,28 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane } return super.onHoverEvent(event); } + + private View getContainerView() { + return (View)getParent(); + } + + @Override + public void showInParent(final ViewGroup parentView) { + removeFromParent(); + parentView.addView(getContainerView()); + } + + @Override + public void removeFromParent() { + final View containerView = getContainerView(); + final ViewGroup currentParent = (ViewGroup)containerView.getParent(); + if (currentParent != null) { + currentParent.removeView(containerView); + } + } + + @Override + public boolean isShowingInParent() { + return (getContainerView().getParent() != null); + } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysPanel.java b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysPanel.java index b511db9d3..5a7e60e30 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysPanel.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysPanel.java @@ -8,17 +8,10 @@ package helium314.keyboard.keyboard; import android.view.View; import android.view.ViewGroup; -import helium314.keyboard.keyboard.emoji.EmojiViewCallback; +import helium314.keyboard.keyboard.emoji.OnKeyEventListener; public interface PopupKeysPanel { interface Controller { - /** - * Set the layout gravity. - * @param layoutGravity requested by the popup - */ - default void setLayoutGravity(int layoutGravity) { - } - /** * Add the {@link PopupKeysPanel} to the target view. * @param panel the panel to be shown. @@ -66,18 +59,19 @@ public interface PopupKeysPanel { * Initializes the layout and event handling of this {@link PopupKeysPanel} and calls the * controller's onShowPopupKeysPanel to add the panel's container view. * Same as {@link PopupKeysPanel#showPopupKeysPanel(View, Controller, int, int, KeyboardActionListener)}, - * but with a {@link EmojiViewCallback}. + * but with a {@link OnKeyEventListener}. * * @param parentView the parent view of this {@link PopupKeysPanel} * @param controller the controller that can dismiss this {@link PopupKeysPanel} * @param pointX x coordinate of this {@link PopupKeysPanel} * @param pointY y coordinate of this {@link PopupKeysPanel} - * @param emojiViewCallback to receive keyboard actions from this {@link PopupKeysPanel}. + * @param listener the listener that will receive keyboard action from this + * {@link PopupKeysPanel}. */ // TODO: Currently the PopupKeysPanel is inside a container view that is added to the parent. // Consider the simpler approach of placing the PopupKeysPanel itself into the parent view. void showPopupKeysPanel(View parentView, Controller controller, int pointX, - int pointY, EmojiViewCallback emojiViewCallback); + int pointY, OnKeyEventListener listener); /** * Dismisses the popup keys panel and calls the controller's onDismissPopupKeysPanel to remove @@ -133,35 +127,20 @@ public interface PopupKeysPanel { */ int translateY(int y); - default View getContainerView() { - return (View) ((View) this).getParent(); - } - /** * Show this {@link PopupKeysPanel} in the parent view. * * @param parentView the {@link ViewGroup} that hosts this {@link PopupKeysPanel}. */ - default void showInParent(ViewGroup parentView) { - removeFromParent(); - parentView.addView(getContainerView()); - } + void showInParent(ViewGroup parentView); /** * Remove this {@link PopupKeysPanel} from the parent view. */ - default void removeFromParent() { - final View containerView = getContainerView(); - final ViewGroup currentParent = (ViewGroup)containerView.getParent(); - if (currentParent != null) { - currentParent.removeView(containerView); - } - } + void removeFromParent(); /** * Return whether the panel is currently being shown. */ - default boolean isShowingInParent() { - return getContainerView().getParent() != null; - } + boolean isShowingInParent(); } diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupTextView.java b/app/src/main/java/helium314/keyboard/keyboard/PopupTextView.java deleted file mode 100644 index d20f62559..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupTextView.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * modified - * SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only - */ - -package helium314.keyboard.keyboard; - -import android.content.Context; -import android.graphics.Typeface; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.widget.TextView; -import helium314.keyboard.keyboard.emoji.EmojiViewCallback; -import helium314.keyboard.keyboard.internal.KeyDrawParams; -import helium314.keyboard.latin.R; -import helium314.keyboard.latin.common.ColorType; -import helium314.keyboard.latin.common.CoordinateUtils; -import helium314.keyboard.latin.settings.Settings; - - -/** - * A view that displays popup text. - */ -public class PopupTextView extends TextView implements PopupKeysPanel { - private final int[] mCoordinates = CoordinateUtils.newInstance(); - private final Typeface mTypeface; - private Controller mController = EMPTY_CONTROLLER; - private int mOriginX; - private int mOriginY; - private Key mKey; - private EmojiViewCallback mEmojiViewCallback; - - public PopupTextView(final Context context, final AttributeSet attrs) { - this(context, attrs, R.attr.popupKeysKeyboardViewStyle); - } - - public PopupTextView(final Context context, final AttributeSet attrs, - final int defStyle) { - super(context, attrs, defStyle); - mTypeface = Settings.getInstance().getCustomTypeface(); - } - - public void setKeyDrawParams(Key key, KeyDrawParams drawParams) { - mKey = key; - Settings.getValues().mColors.setBackground(this, ColorType.KEY_PREVIEW_BACKGROUND); - setTextColor(drawParams.mPreviewTextColor); - setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectHintTextSize(drawParams) << 1); - setTypeface(mTypeface == null ? key.selectTypeface(drawParams) : mTypeface); - } - - @Override - public void showPopupKeysPanel(final View parentView, final Controller controller, - final int pointX, final int pointY, final KeyboardActionListener listener) { - showPopupKeysPanelInternal(parentView, controller, pointX, pointY); - } - - @Override - public void showPopupKeysPanel(final View parentView, final Controller controller, - final int pointX, final int pointY, final EmojiViewCallback emojiViewCallback) { - mEmojiViewCallback = emojiViewCallback; - showPopupKeysPanelInternal(parentView, controller, pointX, pointY); - } - - private void showPopupKeysPanelInternal(final View parentView, final Controller controller, - final int pointX, final int pointY) { - mController = controller; - final View container = getContainerView(); - // The coordinates of panel's left-top corner in parentView's coordinate system. - // We need to consider background drawable paddings. - final int x = pointX - getMeasuredWidth() / 2 - container.getPaddingLeft() - getPaddingLeft(); - final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom() - + getPaddingBottom(); - - parentView.getLocationInWindow(mCoordinates); - // Ensure the horizontal position of the panel does not extend past the parentView edges. - final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth(); - final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates); - final int panelY = y + CoordinateUtils.y(mCoordinates); - container.setX(panelX); - container.setY(panelY); - - mOriginX = x + container.getPaddingLeft(); - mOriginY = y + container.getPaddingTop(); - controller.setLayoutGravity(Gravity.NO_GRAVITY); - controller.onShowPopupKeysPanel(this); - } - - @Override - public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) { - } - - @Override - public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) { - } - - @Override - public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) { - mEmojiViewCallback.onReleaseKey(mKey); - } - - @Override - public void dismissPopupKeysPanel() { - if (!isShowingInParent()) { - return; - } - mController.onDismissPopupKeysPanel(); - } - - @Override - public int translateX(final int x) { - return x - mOriginX; - } - - @Override - public int translateY(final int y) { - return y - mOriginY; - } -} diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java index eea566a72..6e9c4a36c 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java @@ -112,12 +112,12 @@ final class DynamicGridKeyboard extends Keyboard { } public int getDynamicOccupiedHeight() { - final int row = (mGridKeys.size() - 1) / getOccupiedColumnCount() + 1; + final int row = (mGridKeys.size() - 1) / mColumnsNum + 1; return row * mVerticalStep; } - public int getOccupiedColumnCount() { - return mColumnsNum - mEmptyColumnIndices.size(); + public int getColumnsCount() { + return mColumnsNum; } public void addPendingKey(final Key usedKey) { diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java index 413a6b8f6..d2a7213fd 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java @@ -324,7 +324,7 @@ final class EmojiCategory { final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs, mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), 0, 0, ResourceUtils.getKeyboardWidth(mContext, Settings.getValues())); - return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getOccupiedColumnCount(); + return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getColumnsCount(); } private static final Comparator EMOJI_KEY_COMPARATOR = (lhs, rhs) -> { diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java index cc1e0a359..47e0d3b83 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java @@ -13,9 +13,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Handler; import android.util.AttributeSet; -import android.view.Gravity; -import android.widget.LinearLayout; -import helium314.keyboard.keyboard.PopupTextView; import helium314.keyboard.latin.utils.Log; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -56,18 +53,14 @@ public final class EmojiPageKeyboardView extends KeyboardView implements private static final long KEY_PRESS_DELAY_TIME = 250; // msec private static final long KEY_RELEASE_DELAY_TIME = 30; // msec - private static final EmojiViewCallback EMPTY_EMOJI_VIEW_CALLBACK = new EmojiViewCallback() { + private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() { @Override public void onPressKey(final Key key) {} @Override public void onReleaseKey(final Key key) {} - @Override - public String getDescription(String emoji) { - return null; - } }; - private EmojiViewCallback mEmojiViewCallback = EMPTY_EMOJI_VIEW_CALLBACK; + private OnKeyEventListener mListener = EMPTY_LISTENER; private final KeyDetector mKeyDetector = new KeyDetector(); private KeyboardAccessibilityDelegate mAccessibilityDelegate; @@ -81,8 +74,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements // More keys keyboard private final View mPopupKeysKeyboardContainer; - private final PopupTextView mDescriptionView; - private final PopupKeysKeyboardView mPopupKeysKeyboardView; private final WeakHashMap mPopupKeysKeyboardCache = new WeakHashMap<>(); private final boolean mConfigShowPopupKeysKeyboardAtTouchedPoint; private final ViewGroup mPopupKeysPlacerView; @@ -111,8 +102,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements final LayoutInflater inflater = LayoutInflater.from(getContext()); mPopupKeysKeyboardContainer = inflater.inflate(popupKeysKeyboardLayoutId, null); - mDescriptionView = mPopupKeysKeyboardContainer.findViewById(R.id.description_view); - mPopupKeysKeyboardView = mPopupKeysKeyboardContainer.findViewById(R.id.popup_keys_keyboard_view); } @Override @@ -157,8 +146,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements } } - public void setEmojiViewCallback(final EmojiViewCallback emojiViewCallback) { - mEmojiViewCallback = emojiViewCallback; + public void setOnKeyEventListener(final OnKeyEventListener listener) { + mListener = listener; } /** @@ -180,8 +169,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements } @Nullable - private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key) { - mPopupKeysKeyboardView.setVisibility(GONE); + public PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, final int lastX, final int lastY) { final PopupKeySpec[] popupKeys = key.getPopupKeys(); if (popupKeys == null) { return null; @@ -194,9 +182,21 @@ public final class EmojiPageKeyboardView extends KeyboardView implements mPopupKeysKeyboardCache.put(key, popupKeysKeyboard); } - mPopupKeysKeyboardView.setKeyboard(popupKeysKeyboard); - mPopupKeysKeyboardView.setVisibility(VISIBLE); - return mPopupKeysKeyboardView; + final View container = mPopupKeysKeyboardContainer; + final PopupKeysKeyboardView popupKeysKeyboardView = container.findViewById(R.id.popup_keys_keyboard_view); + popupKeysKeyboardView.setKeyboard(popupKeysKeyboard); + container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + final int[] lastCoords = CoordinateUtils.newCoordinateArray(1, lastX, lastY); + // The popup keys keyboard is usually horizontally aligned with the center of the parent key. + // If showPopupKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more + // keys keyboard is placed at the touch point of the parent key. + final int pointX = mConfigShowPopupKeysKeyboardAtTouchedPoint + ? CoordinateUtils.x(lastCoords) + : key.getX() + key.getWidth() / 2; + final int pointY = key.getY(); + popupKeysKeyboardView.showPopupKeysPanel(this, this, pointX, pointY, mListener); + return popupKeysKeyboardView; } private void dismissPopupKeysPanel() { @@ -209,17 +209,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements return mPopupKeysPanel != null; } - @Override - public void setLayoutGravity(int layoutGravity) { - var layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.gravity = mDescriptionView.getMeasuredWidth() > mPopupKeysKeyboardView.getMeasuredWidth()? - layoutGravity : Gravity.CENTER_HORIZONTAL; - mPopupKeysKeyboardContainer.setLayoutParams(layoutParams); - mDescriptionView.setLayoutParams(layoutParams); - mPopupKeysKeyboardView.setLayoutParams(layoutParams); - } - @Override public void onShowPopupKeysPanel(final PopupKeysPanel panel) { // install placer view only when needed instead of when this @@ -301,11 +290,9 @@ public final class EmojiPageKeyboardView extends KeyboardView implements return; } - var descriptionPanel = showDescription(key); - final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key); - final int x = mLastX; final int y = mLastY; + final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key, x, y); if (popupKeysPanel != null) { final int translatedX = popupKeysPanel.translateX(x); final int translatedY = popupKeysPanel.translateY(y); @@ -314,34 +301,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements // want any scroll to append during this entire input. disallowParentInterceptTouchEvent(true); } - - if (popupKeysPanel != null || descriptionPanel != null) { - mPopupKeysKeyboardContainer.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - final int[] lastCoords = CoordinateUtils.newCoordinateArray(1, x, y); - // The popup keys keyboard is usually horizontally aligned with the center of the parent key. - // If showPopupKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more - // keys keyboard is placed at the touch point of the parent key. - final int pointX = mConfigShowPopupKeysKeyboardAtTouchedPoint - ? CoordinateUtils.x(lastCoords) - : key.getX() + key.getWidth() / 2; - final int pointY = key.getY() - getKeyboard().mVerticalGap; - (popupKeysPanel != null? popupKeysPanel : descriptionPanel) - .showPopupKeysPanel(this, this, pointX, pointY, mEmojiViewCallback); - } - } - - private PopupKeysPanel showDescription(Key key) { - mDescriptionView.setVisibility(GONE); - var description = mEmojiViewCallback.getDescription(key.getLabel()); - if (description == null) { - return null; - } - - mDescriptionView.setText(description); - mDescriptionView.setKeyDrawParams(key, getKeyDrawParams()); - mDescriptionView.setVisibility(VISIBLE); - return mDescriptionView; } private void registerPress(final Key key) { @@ -359,7 +318,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements releasedKey.onReleased(); invalidateKey(releasedKey); if (withKeyRegistering) { - mEmojiViewCallback.onReleaseKey(releasedKey); + mListener.onReleaseKey(releasedKey); } } @@ -367,7 +326,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements mPendingKeyDown = null; pressedKey.onPressed(); invalidateKey(pressedKey); - mEmojiViewCallback.onPressKey(pressedKey); + mListener.onPressKey(pressedKey); } public void releaseCurrentKey(final boolean withKeyRegistering) { diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesAdapter.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesAdapter.java index 4172d3db9..7f7b63614 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesAdapter.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesAdapter.java @@ -7,6 +7,7 @@ package helium314.keyboard.keyboard.emoji; import helium314.keyboard.latin.utils.Log; +import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,13 +23,13 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO else -> 0 } - val tlds = mutableListOf(Key.POPUP_KEYS_HAS_LABELS) + val tlds = getLocaleTlds(locale) init { readStream(dataStream, false, true) @@ -84,7 +84,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) { READER_MODE_EXTRA_KEYS -> if (!onlyPopupKeys) addExtraKey(line.split(colonSpaceRegex, 2)) READER_MODE_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2)) READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace() - READER_MODE_TLD -> tlds.addAll(SpacedTokens(line).map { ".$it" }) + READER_MODE_TLD -> SpacedTokens(line).forEach { tlds.add(".$it") } } } } @@ -156,16 +156,6 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) { } } - fun addLocaleTlds(locale: Locale) { - tlds.add(0, comTld) - val ccLower = locale.country.lowercase() - if (ccLower.isNotEmpty() && locale.language != SubtypeLocaleUtils.NO_LANGUAGE) { - specialCountryTlds[ccLower]?.let { tlds.addAll(SpacedTokens(it)) } ?: tlds.add(".$ccLower") - } - if ((locale.language != "en" && euroLocales.matches(locale.language)) || euroCountries.matches(locale.country)) - tlds.add(".eu") - tlds.addAll(SpacedTokens(otherDefaultTlds)) - } } private fun addFixedColumnOrder(popupKeys: MutableCollection) { @@ -215,7 +205,6 @@ private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, popup POPUP_KEYS_MORE -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_more.txt"), false) POPUP_KEYS_ALL -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_all.txt"), false) } - lkt.addLocaleTlds(params.mId.locale) return lkt } @@ -231,6 +220,28 @@ private fun getStreamForLocale(locale: Locale, context: Context) = } } +private fun getLocaleTlds(locale: Locale): LinkedHashSet { + val tlds = getDefaultTlds(locale) + val ccLower = locale.country.lowercase() + if (ccLower.isEmpty() || locale.language == SubtypeLocaleUtils.NO_LANGUAGE) + return tlds + specialCountryTlds.forEach { + if (ccLower != it.first) return@forEach + tlds.addAll(SpacedTokens(it.second)) + return@getLocaleTlds tlds + } + tlds.add(".$ccLower") + return tlds +} + +private fun getDefaultTlds(locale: Locale): LinkedHashSet { + val tlds = linkedSetOf() + tlds.addAll(SpacedTokens(defaultTlds)) + if ((locale.language != "en" && euroLocales.matches(locale.language)) || euroCountries.matches(locale.country)) + tlds.add(".eu") + return tlds +} + fun clearCache() = localeKeyboardInfosCache.clear() // cache the texts, so they don't need to be read over and over @@ -330,7 +341,7 @@ const val POPUP_KEYS_NORMAL = "normal" private const val LOCALE_TEXTS_FOLDER = "locale_key_texts" // either tld is not simply lowercase ISO 3166-1 code, or there are multiple according to some list -private val specialCountryTlds = hashMapOf( +private val specialCountryTlds = listOf( "bd" to ".bd .com.bd", "bq" to ".bq .an .nl", "bl" to ".bl .gp .fr", @@ -340,5 +351,4 @@ private val specialCountryTlds = hashMapOf( "mf" to ".mf .gp .fr", "tl" to ".tl .tp", ) -private const val comTld = ".com" -private const val otherDefaultTlds = ".gov .edu .org .net" +private const val defaultTlds = ".com .gov .edu .org .net" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index eccf62ebb..1488faaf5 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -175,12 +175,6 @@ object KeyCode { const val META_LEFT = -10048 const val META_RIGHT = -10049 - - // Intents - const val SEND_INTENT_ONE = -20000 - const val SEND_INTENT_TWO = -20001 - const val SEND_INTENT_THREE = -20002 - /** to make sure a FlorisBoard code works when reading a JSON layout */ fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) { // working @@ -196,7 +190,7 @@ object KeyCode { ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP, PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT, MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK, - TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO, SEND_INTENT_THREE, + TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT -> this // conversion @@ -216,11 +210,66 @@ object KeyCode { // todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0 /** - * Convert an internal keyCode to a KeyEvent.KEYCODE_. - * Positive codes are passed through, unknown negative codes result in KeyEvent.KEYCODE_UNKNOWN. + * Convert a keyCode / codePoint to a KeyEvent.KEYCODE_. + * Fallback to KeyEvent.KEYCODE_UNKNOWN. * To be uses for fake hardware key press. - */ - @JvmStatic fun keyCodeToKeyEventCode(keyCode: Int) = when (keyCode) { + * */ + fun Int.toKeyEventCode(): Int = if (this > 0) + when (this.toChar().uppercaseChar()) { + '/' -> KeyEvent.KEYCODE_SLASH + '\\' -> KeyEvent.KEYCODE_BACKSLASH + ';' -> KeyEvent.KEYCODE_SEMICOLON + ',' -> KeyEvent.KEYCODE_COMMA + '.' -> KeyEvent.KEYCODE_PERIOD + '\'' -> KeyEvent.KEYCODE_APOSTROPHE + '`' -> KeyEvent.KEYCODE_GRAVE + '*' -> KeyEvent.KEYCODE_STAR + ']' -> KeyEvent.KEYCODE_RIGHT_BRACKET + '[' -> KeyEvent.KEYCODE_LEFT_BRACKET + '+' -> KeyEvent.KEYCODE_PLUS + '-' -> KeyEvent.KEYCODE_MINUS + '=' -> KeyEvent.KEYCODE_EQUALS + '\n' -> KeyEvent.KEYCODE_ENTER + '\t' -> KeyEvent.KEYCODE_TAB + '0' -> KeyEvent.KEYCODE_0 + '1' -> KeyEvent.KEYCODE_1 + '2' -> KeyEvent.KEYCODE_2 + '3' -> KeyEvent.KEYCODE_3 + '4' -> KeyEvent.KEYCODE_4 + '5' -> KeyEvent.KEYCODE_5 + '6' -> KeyEvent.KEYCODE_6 + '7' -> KeyEvent.KEYCODE_7 + '8' -> KeyEvent.KEYCODE_8 + '9' -> KeyEvent.KEYCODE_9 + 'A' -> KeyEvent.KEYCODE_A + 'B' -> KeyEvent.KEYCODE_B + 'C' -> KeyEvent.KEYCODE_C + 'D' -> KeyEvent.KEYCODE_D + 'E' -> KeyEvent.KEYCODE_E + 'F' -> KeyEvent.KEYCODE_F + 'G' -> KeyEvent.KEYCODE_G + 'H' -> KeyEvent.KEYCODE_H + 'I' -> KeyEvent.KEYCODE_I + 'J' -> KeyEvent.KEYCODE_J + 'K' -> KeyEvent.KEYCODE_K + 'L' -> KeyEvent.KEYCODE_L + 'M' -> KeyEvent.KEYCODE_M + 'N' -> KeyEvent.KEYCODE_N + 'O' -> KeyEvent.KEYCODE_O + 'P' -> KeyEvent.KEYCODE_P + 'Q' -> KeyEvent.KEYCODE_Q + 'R' -> KeyEvent.KEYCODE_R + 'S' -> KeyEvent.KEYCODE_S + 'T' -> KeyEvent.KEYCODE_T + 'U' -> KeyEvent.KEYCODE_U + 'V' -> KeyEvent.KEYCODE_V + 'W' -> KeyEvent.KEYCODE_W + 'X' -> KeyEvent.KEYCODE_X + 'Y' -> KeyEvent.KEYCODE_Y + 'Z' -> KeyEvent.KEYCODE_Z + else -> KeyEvent.KEYCODE_UNKNOWN + } + else when (this) { ARROW_UP -> KeyEvent.KEYCODE_DPAD_UP ARROW_RIGHT -> KeyEvent.KEYCODE_DPAD_RIGHT ARROW_DOWN -> KeyEvent.KEYCODE_DPAD_DOWN @@ -254,67 +303,6 @@ object KeyCode { F10 -> KeyEvent.KEYCODE_F10 F11 -> KeyEvent.KEYCODE_F11 F12 -> KeyEvent.KEYCODE_F12 - else -> if (keyCode < 0) KeyEvent.KEYCODE_UNKNOWN else keyCode - } - - // todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0 - /** - * Convert a codePoint to a KeyEvent.KEYCODE_. - * Fallback to KeyEvent.KEYCODE_UNKNOWN. - * To be uses for fake hardware key press. - */ - @JvmStatic fun codePointToKeyEventCode(codePoint: Int): Int = when (codePoint.toChar().uppercaseChar()) { - '/' -> KeyEvent.KEYCODE_SLASH - '\\' -> KeyEvent.KEYCODE_BACKSLASH - ';' -> KeyEvent.KEYCODE_SEMICOLON - ',' -> KeyEvent.KEYCODE_COMMA - '.' -> KeyEvent.KEYCODE_PERIOD - '\'' -> KeyEvent.KEYCODE_APOSTROPHE - '`' -> KeyEvent.KEYCODE_GRAVE - '*' -> KeyEvent.KEYCODE_STAR - ']' -> KeyEvent.KEYCODE_RIGHT_BRACKET - '[' -> KeyEvent.KEYCODE_LEFT_BRACKET - '+' -> KeyEvent.KEYCODE_PLUS - '-' -> KeyEvent.KEYCODE_MINUS - '=' -> KeyEvent.KEYCODE_EQUALS - '\n' -> KeyEvent.KEYCODE_ENTER - '\t' -> KeyEvent.KEYCODE_TAB - '0' -> KeyEvent.KEYCODE_0 - '1' -> KeyEvent.KEYCODE_1 - '2' -> KeyEvent.KEYCODE_2 - '3' -> KeyEvent.KEYCODE_3 - '4' -> KeyEvent.KEYCODE_4 - '5' -> KeyEvent.KEYCODE_5 - '6' -> KeyEvent.KEYCODE_6 - '7' -> KeyEvent.KEYCODE_7 - '8' -> KeyEvent.KEYCODE_8 - '9' -> KeyEvent.KEYCODE_9 - 'A' -> KeyEvent.KEYCODE_A - 'B' -> KeyEvent.KEYCODE_B - 'C' -> KeyEvent.KEYCODE_C - 'D' -> KeyEvent.KEYCODE_D - 'E' -> KeyEvent.KEYCODE_E - 'F' -> KeyEvent.KEYCODE_F - 'G' -> KeyEvent.KEYCODE_G - 'H' -> KeyEvent.KEYCODE_H - 'I' -> KeyEvent.KEYCODE_I - 'J' -> KeyEvent.KEYCODE_J - 'K' -> KeyEvent.KEYCODE_K - 'L' -> KeyEvent.KEYCODE_L - 'M' -> KeyEvent.KEYCODE_M - 'N' -> KeyEvent.KEYCODE_N - 'O' -> KeyEvent.KEYCODE_O - 'P' -> KeyEvent.KEYCODE_P - 'Q' -> KeyEvent.KEYCODE_Q - 'R' -> KeyEvent.KEYCODE_R - 'S' -> KeyEvent.KEYCODE_S - 'T' -> KeyEvent.KEYCODE_T - 'U' -> KeyEvent.KEYCODE_U - 'V' -> KeyEvent.KEYCODE_V - 'W' -> KeyEvent.KEYCODE_W - 'X' -> KeyEvent.KEYCODE_X - 'Y' -> KeyEvent.KEYCODE_Y - 'Z' -> KeyEvent.KEYCODE_Z else -> KeyEvent.KEYCODE_UNKNOWN } } diff --git a/app/src/main/java/helium314/keyboard/latin/Dictionary.java b/app/src/main/java/helium314/keyboard/latin/Dictionary.java index f65893edf..b9e9f0344 100644 --- a/app/src/main/java/helium314/keyboard/latin/Dictionary.java +++ b/app/src/main/java/helium314/keyboard/latin/Dictionary.java @@ -11,7 +11,6 @@ import java.util.Locale; import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; import helium314.keyboard.latin.common.ComposedData; -import helium314.keyboard.latin.makedict.WordProperty; import helium314.keyboard.latin.settings.SettingsValuesForSuggestion; /** @@ -178,10 +177,6 @@ public abstract class Dictionary { }; } - public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) { - return null; - } - /** * Not a true dictionary. A placeholder used to indicate suggestions that don't come from any * real dictionary. diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt index c64158b01..325ee3595 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt @@ -397,10 +397,7 @@ class DictionaryFacilitatorImpl : DictionaryFacilitator { // This is not too bad, but it delays adding in case a user wants to fill a dictionary using this functionality if (userHistoryDict.getFrequency(word) > 120) { scope.launch { - // adding can throw IllegalArgumentException: Unknown URL content://user_dictionary/words - // https://stackoverflow.com/q/41474623 https://github.com/AnySoftKeyboard/AnySoftKeyboard/issues/490 - // apparently some devices don't have a dictionary? or it's just sporadic hiccups? - runCatching { UserDictionary.Words.addWord(userDict.mContext, word, 250, null, dictionaryGroup.locale) } + UserDictionary.Words.addWord(userDict.mContext, word, 250, null, dictionaryGroup.locale) } } } @@ -734,7 +731,7 @@ private class DictionaryGroup( else { val file = File(context.filesDir.absolutePath + File.separator + "blacklists" + File.separator + locale.toLanguageTag() + ".txt") if (file.isDirectory) file.delete() // this apparently was an issue in some versions - if (file.parentFile?.exists() == true || file.parentFile?.mkdirs() == true) file + if (file.parentFile?.mkdirs() == true) file else null } diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFactory.kt b/app/src/main/java/helium314/keyboard/latin/DictionaryFactory.kt index 576bd90a4..bd86960b3 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFactory.kt +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFactory.kt @@ -64,26 +64,10 @@ object DictionaryFactory { * if the dictionary type already exists in [dicts], the [file] is skipped */ private fun checkAndAddDictionaryToListNewType(file: File, dicts: MutableList, locale: Locale) { - val dictionary = getDictionary(file, locale) ?: return - if (dicts.any { it.mDictType == dictionary.mDictType }) { - dictionary.close() - return - } - dicts.add(dictionary) - } - - @JvmStatic - fun getDictionary( - file: File, - locale: Locale - ): Dictionary? { - if (!file.isFile) return null - val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) - if (header == null) { - killDictionary(file) - return null - } + if (!file.isFile) return + val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) ?: return killDictionary(file) val dictType = header.mIdString.split(":").first() + if (dicts.any { it.mDictType == dictType }) return val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary( file.absolutePath, 0, file.length(), false, locale, dictType ) @@ -91,13 +75,14 @@ object DictionaryFactory { if (readOnlyBinaryDictionary.isValidDictionary) { if (locale.language == "ko") { // Use KoreanDictionary for Korean locale - return KoreanDictionary(readOnlyBinaryDictionary) + dicts.add(KoreanDictionary(readOnlyBinaryDictionary)) + } else { + dicts.add(readOnlyBinaryDictionary) } - return readOnlyBinaryDictionary + } else { + readOnlyBinaryDictionary.close() + killDictionary(file) } - readOnlyBinaryDictionary.close() - killDictionary(file) - return null } private fun killDictionary(file: File) { diff --git a/app/src/main/java/helium314/keyboard/latin/EmojiAltPhysicalKeyDetector.java b/app/src/main/java/helium314/keyboard/latin/EmojiAltPhysicalKeyDetector.java index 9b06420ee..79bfeeb24 100644 --- a/app/src/main/java/helium314/keyboard/latin/EmojiAltPhysicalKeyDetector.java +++ b/app/src/main/java/helium314/keyboard/latin/EmojiAltPhysicalKeyDetector.java @@ -23,7 +23,7 @@ import java.util.List; /** * A class for detecting Emoji-Alt physical key. */ -public final class EmojiAltPhysicalKeyDetector { +final class EmojiAltPhysicalKeyDetector { private static final String TAG = "EmojiAltPhysKeyDetector"; private static final boolean DEBUG = false; diff --git a/app/src/main/java/helium314/keyboard/latin/KoreanDictionary.java b/app/src/main/java/helium314/keyboard/latin/KoreanDictionary.java index 2b6722777..9b791148c 100644 --- a/app/src/main/java/helium314/keyboard/latin/KoreanDictionary.java +++ b/app/src/main/java/helium314/keyboard/latin/KoreanDictionary.java @@ -4,7 +4,6 @@ package helium314.keyboard.latin; import helium314.keyboard.event.HangulCombiner; import helium314.keyboard.latin.common.ComposedData; -import helium314.keyboard.latin.makedict.WordProperty; import helium314.keyboard.latin.settings.SettingsValuesForSuggestion; import java.text.Normalizer; @@ -73,11 +72,6 @@ public class KoreanDictionary extends Dictionary { return mDictionary.getMaxFrequencyOfExactMatches(processInput(word)); } - @Override - public WordProperty getWordProperty(String word, boolean isBeginningOfSentence) { - return mDictionary.getWordProperty(processInput(word), isBeginningOfSentence); - } - @Override protected boolean same(char[] word, int length, String typedWord) { word = processInput(new String(word)).toCharArray(); diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index 9e2ed3c72..670e820bd 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -26,6 +26,7 @@ import android.os.Process; import android.text.InputType; import android.util.PrintWriterPrinter; import android.util.Printer; +import android.util.SparseArray; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; @@ -50,6 +51,9 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.common.InsetsOutlineProvider; import helium314.keyboard.dictionarypack.DictionaryPackConstants; import helium314.keyboard.event.Event; +import helium314.keyboard.event.HangulEventDecoder; +import helium314.keyboard.event.HardwareEventDecoder; +import helium314.keyboard.event.HardwareKeyboardEventDecoder; import helium314.keyboard.event.InputTransaction; import helium314.keyboard.keyboard.Keyboard; import helium314.keyboard.keyboard.KeyboardId; @@ -64,6 +68,7 @@ import helium314.keyboard.latin.common.InputPointers; import helium314.keyboard.latin.common.LocaleUtils; import helium314.keyboard.latin.common.ViewOutlineProviderUtilsKt; import helium314.keyboard.latin.define.DebugFlags; +import helium314.keyboard.latin.define.ProductionFlags; import helium314.keyboard.latin.inputlogic.InputLogic; import helium314.keyboard.latin.personalization.PersonalizationHelper; import helium314.keyboard.latin.settings.Settings; @@ -129,6 +134,9 @@ public class LatinIME extends InputMethodService implements private final DictionaryFacilitator mDictionaryFacilitator = DictionaryFacilitatorProvider.getDictionaryFacilitator(false); final InputLogic mInputLogic = new InputLogic(this, this, mDictionaryFacilitator); + // We expect to have only one decoder in almost all cases, hence the default capacity of 1. + // If it turns out we need several, it will get grown seamlessly. + final SparseArray mHardwareEventDecoders = new SparseArray<>(1); // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. private View mInputView; @@ -138,6 +146,7 @@ public class LatinIME extends InputMethodService implements private RichInputMethodManager mRichImm; final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); + private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector; private final StatsUtilsManager mStatsUtilsManager; // Working variable for {@link #startShowingInputView()} and // {@link #onEvaluateInputViewShown()}. @@ -270,7 +279,9 @@ public class LatinIME extends InputMethodService implements msg.arg2 /* remainingTries */, this /* handler */)) { // If we were able to reset the caches, then we can reload the keyboard. // Otherwise, we'll do it when we can. - latinIme.mKeyboardSwitcher.reloadMainKeyboard(); + latinIme.mKeyboardSwitcher.loadKeyboard(latinIme.getCurrentInputEditorInfo(), + settingsValues, latinIme.getCurrentAutoCapsState(), + latinIme.getCurrentRecapitalizeState()); } break; case MSG_WAIT_FOR_DICTIONARY_LOAD: @@ -626,7 +637,6 @@ public class LatinIME extends InputMethodService implements @Override public void onCreate() { - mSettings.startListener(); KeyboardIconsSet.Companion.getInstance().loadIcons(this); mRichImm = RichInputMethodManager.getInstance(); AudioAndHapticFeedbackManager.init(this); @@ -654,8 +664,7 @@ public class LatinIME extends InputMethodService implements final IntentFilter newDictFilter = new IntentFilter(); newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); - // RECEIVER_EXPORTED is necessary because apparently Android 15 (and others?) don't recognize if the sender and receiver are the same app, see https://github.com/Helium314/HeliBoard/pull/1756 - ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_EXPORTED); + ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_NOT_EXPORTED); final IntentFilter dictDumpFilter = new IntentFilter(); dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); @@ -1062,7 +1071,7 @@ public class LatinIME extends InputMethodService implements if (isDifferentTextField) { mainKeyboardView.closing(); suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold); - switcher.reloadMainKeyboard(); + switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); if (needToCallLoadKeyboardLater) { // If we need to call loadKeyboard again later, we need to save its state now. The // later call will be done in #retryResetCaches. @@ -1110,7 +1119,6 @@ public class LatinIME extends InputMethodService implements @Override public void onWindowHidden() { super.onWindowHidden(); - Log.i(TAG, "onWindowHidden"); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); if (mainKeyboardView != null) { mainKeyboardView.closing(); @@ -1167,12 +1175,8 @@ public class LatinIME extends InputMethodService implements if (isInputViewShown() && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, composingSpanEnd, settingsValues)) { - // we don't want to update a manually set shift state if selection changed towards one side - // because this may end the manual shift, which is unwanted in case of shift + arrow keys for changing selection - // todo: this is not fully implemented yet, and maybe should be behind a setting - if (mKeyboardSwitcher.getKeyboard() != null && mKeyboardSwitcher.getKeyboard().mId.isAlphabetShiftedManually() - && !((oldSelEnd == newSelEnd && oldSelStart != newSelStart) || (oldSelEnd != newSelEnd && oldSelStart == newSelStart))) - mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); + mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), + getCurrentRecapitalizeState()); } } @@ -1213,7 +1217,6 @@ public class LatinIME extends InputMethodService implements @Override public void hideWindow() { - Log.i(TAG, "hideWindow"); if (hasSuggestionStripView() && mSettings.getCurrent().mToolbarMode == ToolbarMode.EXPANDABLE) mSuggestionStripView.setToolbarVisibility(false); mKeyboardSwitcher.onHideWindow(); @@ -1226,12 +1229,6 @@ public class LatinIME extends InputMethodService implements super.hideWindow(); } - @Override - public void requestHideSelf(int flags) { - super.requestHideSelf(flags); - Log.i(TAG, "requestHideSelf: " + flags); - } - @Override public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) { if (DebugFlags.DEBUG_ENABLED) { @@ -1283,8 +1280,8 @@ public class LatinIME extends InputMethodService implements if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) { // If there is a hardware keyboard and a visible software keyboard view has been hidden, // no visual element will be shown on the screen. - // for some reason setting contentTopInsets and visibleTopInsets broke somewhere along the - // way from OpenBoard to HeliBoard (GH-702, GH-1455), but not setting anything seems to work + outInsets.contentTopInsets = inputHeight; + outInsets.visibleTopInsets = inputHeight; mInsetsUpdater.setInsets(outInsets); return; } @@ -1542,7 +1539,25 @@ public class LatinIME extends InputMethodService implements // Implementation of {@link SuggestionStripView.Listener}. @Override public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) { - mKeyboardActionListener.onCodeInput(codePoint, x, y, isKeyRepeat); + onCodeInput(codePoint, 0, x, y, isKeyRepeat); + } + + public void onCodeInput(final int codePoint, final int metaState, final int x, final int y, final boolean isKeyRepeat) { + if (codePoint < 0) { + switch (codePoint) { + case KeyCode.TOGGLE_AUTOCORRECT -> {mSettings.toggleAutoCorrect(); return; } + case KeyCode.TOGGLE_INCOGNITO_MODE -> {mSettings.toggleAlwaysIncognitoMode(); return; } + } + } + final Event event; + // checking if the character is a combining accent + if (0x300 <= codePoint && codePoint <= 0x35b) { + event = Event.createSoftwareDeadEvent(codePoint, 0, metaState, x, y, null); + } else { + event = createSoftwareKeypressEvent(codePoint, metaState, x, y, isKeyRepeat); + } + + onEvent(event); } // This method is public for testability of LatinIME, but also in the future it should @@ -1559,6 +1574,24 @@ public class LatinIME extends InputMethodService implements mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } + // A helper method to split the code point and the key code. Ultimately, they should not be + // squashed into the same variable, and this method should be removed. + // public for testing, as we don't want to copy the same logic into test code + @NonNull + public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int metaState, + final int keyX, final int keyY, final boolean isKeyRepeat) { + final int keyCode; + final int codePoint; + if (keyCodeOrCodePoint <= 0) { + keyCode = keyCodeOrCodePoint; + codePoint = Event.NOT_A_CODE_POINT; + } else { + keyCode = Event.NOT_A_KEY_CODE; + codePoint = keyCodeOrCodePoint; + } + return Event.createSoftwareKeypressEvent(codePoint, keyCode, metaState, keyX, keyY, isKeyRepeat); + } + public void onTextInput(final String rawText) { // TODO: have the keyboard pass the correct key code when we need it. final Event event = Event.createSoftwareTextEvent(rawText, KeyCode.MULTIPLE_CODE_POINTS); @@ -1730,7 +1763,8 @@ public class LatinIME extends InputMethodService implements loadSettings(); if (mKeyboardSwitcher.getMainKeyboardView() != null) { // Reload keyboard because the current language has been changed. - mKeyboardSwitcher.reloadMainKeyboard(); + mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent(), + getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } } @@ -1798,18 +1832,63 @@ public class LatinIME extends InputMethodService implements feedbackManager.performAudioFeedback(code); } + private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) { + final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId); + if (null != decoder) return decoder; + // TODO: create the decoder according to the specification + final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId); + mHardwareEventDecoders.put(deviceId, newDecoder); + return newDecoder; + } + // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { - if (mKeyboardActionListener.onKeyDown(keyCode, keyEvent)) + if (mEmojiAltPhysicalKeyDetector == null) { + mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector( + getApplicationContext().getResources()); + } + mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent); + if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { + return super.onKeyDown(keyCode, keyEvent); + } + final Event event; + if (mRichImm.getCurrentSubtypeLocale().getLanguage().equals("ko")) { + final RichInputMethodSubtype subtype = mKeyboardSwitcher.getKeyboard() == null + ? mRichImm.getCurrentSubtype() + : mKeyboardSwitcher.getKeyboard().mId.mSubtype; + event = HangulEventDecoder.decodeHardwareKeyEvent(subtype, keyEvent, + () -> getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent)); + } else { + event = getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent); + } + // If the event is not handled by LatinIME, we just pass it to the parent implementation. + // If it's handled, we return true because we did handle it. + if (event.isHandled()) { + mInputLogic.onCodeInput(mSettings.getCurrent(), event, + mKeyboardSwitcher.getKeyboardShiftMode(), + // TODO: this is not necessarily correct for a hardware keyboard right now + mKeyboardSwitcher.getCurrentKeyboardScript(), + mHandler); return true; + } return super.onKeyDown(keyCode, keyEvent); } @Override public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { - if (mKeyboardActionListener.onKeyUp(keyCode, keyEvent)) + if (mEmojiAltPhysicalKeyDetector == null) { + mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector( + getApplicationContext().getResources()); + } + mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent); + if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { + return super.onKeyUp(keyCode, keyEvent); + } + final long keyIdentifier = (long) keyEvent.getDeviceId() << 32 + keyEvent.getKeyCode(); + if (mInputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) { return true; + } return super.onKeyUp(keyCode, keyEvent); } diff --git a/app/src/main/java/helium314/keyboard/latin/ReadOnlyBinaryDictionary.java b/app/src/main/java/helium314/keyboard/latin/ReadOnlyBinaryDictionary.java index f757e87eb..b4cfe5f3a 100644 --- a/app/src/main/java/helium314/keyboard/latin/ReadOnlyBinaryDictionary.java +++ b/app/src/main/java/helium314/keyboard/latin/ReadOnlyBinaryDictionary.java @@ -10,7 +10,6 @@ import com.android.inputmethod.latin.BinaryDictionary; import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; import helium314.keyboard.latin.common.ComposedData; -import helium314.keyboard.latin.makedict.WordProperty; import helium314.keyboard.latin.settings.SettingsValuesForSuggestion; import java.util.ArrayList; @@ -108,18 +107,6 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { return NOT_A_PROBABILITY; } - @Override - public WordProperty getWordProperty(String word, boolean isBeginningOfSentence) { - if (mLock.readLock().tryLock()) { - try { - return mBinaryDictionary.getWordProperty(word, isBeginningOfSentence); - } finally { - mLock.readLock().unlock(); - } - } - return null; - } - @Override public void close() { mLock.writeLock().lock(); diff --git a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java index f7e8bcc03..95beb8683 100644 --- a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java +++ b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java @@ -439,7 +439,7 @@ public final class RichInputConnection implements PrivateCommandPerformer { // test for this explicitly) if (INVALID_CURSOR_POSITION != mExpectedSelStart && (cachedLength >= n || cachedLength >= mExpectedSelStart)) { - final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText.toString()); + final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText); // We call #toString() here to create a temporary object. // In some situations, this method is called on a worker thread, and it's possible // the main thread touches the contents of mComposingText while this worker thread @@ -716,13 +716,8 @@ public final class RichInputConnection implements PrivateCommandPerformer { if (start < 0 || end < 0) { return false; } - if (start > end) { - mExpectedSelStart = end; - mExpectedSelEnd = start; - } else { - mExpectedSelStart = start; - mExpectedSelEnd = end; - } + mExpectedSelStart = start; + mExpectedSelEnd = end; if (isConnected()) { final boolean isIcValid = mIC.setSelection(start, end); if (!isIcValid) { diff --git a/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt b/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt index 4e2d62486..e8941c459 100644 --- a/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt +++ b/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt @@ -7,7 +7,6 @@ import helium314.keyboard.keyboard.Keyboard import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.latin.DictionaryFacilitator.DictionaryInitializationListener import helium314.keyboard.latin.common.ComposedData -import helium314.keyboard.latin.makedict.WordProperty import helium314.keyboard.latin.settings.SettingsValuesForSuggestion import helium314.keyboard.latin.utils.SuggestionResults import java.util.Locale @@ -48,8 +47,6 @@ class SingleDictionaryFacilitator(private val dict: Dictionary) : DictionaryFaci return suggestionResults } - fun getWordProperty(word: String): WordProperty? = dict.getWordProperty(word, false) - // ------------ dummy functionality ---------------- override fun setValidSpellingWordReadCache(cache: LruCache) {} diff --git a/app/src/main/java/helium314/keyboard/latin/common/Constants.kt b/app/src/main/java/helium314/keyboard/latin/common/Constants.kt index 43b9e66dd..17b36d1da 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/Constants.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/Constants.kt @@ -12,5 +12,3 @@ object Links { const val CUSTOM_LAYOUTS = "$GITHUB/discussions/categories/custom-layout" const val CUSTOM_COLORS = "$GITHUB/discussions/categories/custom-colors" } - -val combiningRange = 0x300..0x35b diff --git a/app/src/main/java/helium314/keyboard/latin/define/ProductionFlags.kt b/app/src/main/java/helium314/keyboard/latin/define/ProductionFlags.kt index 9aeb7cc3d..d476a0426 100644 --- a/app/src/main/java/helium314/keyboard/latin/define/ProductionFlags.kt +++ b/app/src/main/java/helium314/keyboard/latin/define/ProductionFlags.kt @@ -7,11 +7,15 @@ package helium314.keyboard.latin.define object ProductionFlags { - const val IS_HARDWARE_KEYBOARD_SUPPORTED = true + const val IS_HARDWARE_KEYBOARD_SUPPORTED = false + // todo: make it work + // was set to true in hangul branch (and there is the hangul hardware event decoder in latinIme) + // but disabled again because this breaks ctrl+c / ctrl+v, and most likely other things + // so it looks like the HardwareKeyboardEventDecoder needs some work before it's ready /** * Include all suggestions from all dictionaries in * [helium314.keyboard.latin.SuggestedWords.mRawSuggestions]. */ const val INCLUDE_RAW_SUGGESTIONS = false -} +} \ No newline at end of file diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 28572fa2f..b1f8b5bca 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -49,7 +49,6 @@ import helium314.keyboard.latin.settings.SpacingAndPunctuations; import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor; import helium314.keyboard.latin.utils.AsyncResultHolder; import helium314.keyboard.latin.utils.InputTypeUtils; -import helium314.keyboard.latin.utils.IntentUtils; import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.RecapitalizeStatus; import helium314.keyboard.latin.utils.ScriptUtils; @@ -90,7 +89,6 @@ public final class InputLogic { private int mDeleteCount; private long mLastKeyTime; - // todo: this is not used, so either remove it or do something with it public final TreeSet mCurrentlyPressedHardwareKeys = new TreeSet<>(); // Keeps track of most recently inserted text (multi-character key) for reverting @@ -401,11 +399,7 @@ public final class InputLogic { // Stop the last recapitalization, if started. mRecapitalizeStatus.stop(); mWordBeingCorrectedByCursor = null; - - // we do not return true if - final boolean oneSidedSelectionMove = hasOrHadSelection - && ((oldSelEnd == newSelEnd && oldSelStart != newSelStart) || (oldSelEnd != newSelEnd && oldSelStart == newSelStart)); - return !oneSidedSelectionMove; + return true; } public boolean moveCursorByAndReturnIfInsideComposingWord(int distance) { @@ -649,8 +643,7 @@ public final class InputLogic { */ private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction, final String currentKeyboardScript, final LatinIME.UIHandler handler) { - final int keyCode = event.getMKeyCode(); - switch (keyCode) { + switch (event.getMKeyCode()) { case KeyCode.DELETE: handleBackspaceEvent(event, inputTransaction, currentKeyboardScript); // Backspace is a functional key, but it affects the contents of the editor. @@ -693,7 +686,7 @@ public final class InputLogic { case KeyCode.SHIFT_ENTER: // todo: try using sendDownUpKeyEventWithMetaState() and remove the key code maybe final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER, - keyCode, 0, event.getMX(), event.getMY(), event.isKeyRepeat()); + event.getMKeyCode(), 0, event.getMX(), event.getMY(), event.isKeyRepeat()); handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler); // Shift + Enter is treated as a functional key but it results in adding a new // line, so that does affect the contents of the editor. @@ -724,43 +717,37 @@ public final class InputLogic { if (mConnection.hasSelection()) { mConnection.copyText(true); // fake delete keypress to remove the text - final Event backspaceEvent = Event.createSoftwareKeypressEvent(KeyCode.DELETE, 0, + final Event backspaceEvent = LatinIME.createSoftwareKeypressEvent(KeyCode.DELETE, 0, event.getMX(), event.getMY(), event.isKeyRepeat()); handleBackspaceEvent(backspaceEvent, inputTransaction, currentKeyboardScript); inputTransaction.setDidAffectContents(); } break; case KeyCode.WORD_LEFT: - sendDownUpKeyEventWithMetaState( - ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.META_CTRL_ON | event.getMMetaState()); + sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)? + KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_CTRL_ON); break; case KeyCode.WORD_RIGHT: - sendDownUpKeyEventWithMetaState( - ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.META_CTRL_ON | event.getMMetaState()); + sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)? + KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.META_CTRL_ON); break; case KeyCode.MOVE_START_OF_PAGE: - final int selectionEnd1 = mConnection.getExpectedSelectionEnd(); - final int selectionStart1 = mConnection.getExpectedSelectionStart(); - sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON | event.getMMetaState()); - if (mConnection.getExpectedSelectionStart() == selectionStart1 && mConnection.getExpectedSelectionEnd() == selectionEnd1) { - // unchanged -> try a different method (necessary for compose fields) - final int newEnd = (event.getMMetaState() & KeyEvent.META_SHIFT_MASK) != 0 ? selectionEnd1 : 0; - mConnection.setSelection(0, newEnd); + final int selectionEnd = mConnection.getExpectedSelectionEnd(); + sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON); + if (mConnection.getExpectedSelectionStart() > 0 && mConnection.getExpectedSelectionEnd() == selectionEnd) { + // unchanged, and we're not at the top -> try a different method (necessary for compose fields) + mConnection.setSelection(0, 0); } break; case KeyCode.MOVE_END_OF_PAGE: - final int selectionStart2 = mConnection.getExpectedSelectionStart(); - final int selectionEnd2 = mConnection.getExpectedSelectionEnd(); - sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON | event.getMMetaState()); - if (mConnection.getExpectedSelectionStart() == selectionStart2 && mConnection.getExpectedSelectionEnd() == selectionEnd2) { + final int selectionStart = mConnection.getExpectedSelectionEnd(); + sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON); + if (mConnection.getExpectedSelectionStart() == selectionStart) { // unchanged, try fallback e.g. for compose fields that don't care about ctrl + end // we just move to a very large index, and hope the field is prepared to deal with this // getting the actual length of the text for setting the correct position can be tricky for some apps... try { - final int newStart = (event.getMMetaState() & KeyEvent.META_SHIFT_MASK) != 0 ? selectionStart2 : Integer.MAX_VALUE; - mConnection.setSelection(newStart, Integer.MAX_VALUE); + mConnection.setSelection(Integer.MAX_VALUE, Integer.MAX_VALUE); } catch (Exception e) { // better catch potential errors and just do nothing in this case Log.i(TAG, "error when trying to move cursor to last position: " + e); @@ -779,10 +766,8 @@ public final class InputLogic { case KeyCode.TIMESTAMP: mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME)); break; - case KeyCode.SEND_INTENT_ONE, KeyCode.SEND_INTENT_TWO, KeyCode.SEND_INTENT_THREE: - IntentUtils.handleSendIntentKey(mLatinIME, event.getMKeyCode()); case KeyCode.IME_HIDE_UI: - mLatinIME.requestHideSelf(0); + mLatinIME.hideWindow(); break; case KeyCode.VOICE_INPUT: // switching to shortcut IME, shift state, keyboard,... is handled by LatinIME, @@ -792,20 +777,23 @@ public final class InputLogic { case KeyCode.CAPS_LOCK, KeyCode.EMOJI, KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE: break; default: - if (KeyCode.INSTANCE.isModifier(keyCode)) - return; // continuation of previous switch case above, but modifiers are held in a separate place - final int keyEventCode = keyCode > 0 - ? keyCode - : event.getMCodePoint() >= 0 ? KeyCode.codePointToKeyEventCode(event.getMCodePoint()) - : KeyCode.keyCodeToKeyEventCode(keyCode); - if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) { - sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState()); - return; + if (KeyCode.INSTANCE.isModifier(event.getMKeyCode())) + return; // continuation of previous switch case, but modifiers are in a separate place + if (event.getMMetaState() != 0) { + // need to convert codepoint to KeyEvent.KEYCODE_ + final int codeToConvert = event.getMKeyCode() < 0 ? event.getMKeyCode() : event.getMCodePoint(); + int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(codeToConvert); + if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) + sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState()); + return; // never crash if user inputs sth we don't have a KeyEvent.KEYCODE for + } else if (event.getMKeyCode() < 0) { + int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(event.getMKeyCode()); + if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) { + sendDownUpKeyEvent(keyEventCode); + return; + } } - // unknown event - Log.e(TAG, "unknown event, key code: "+keyCode+", meta: "+event.getMMetaState()); - if (DebugFlags.DEBUG_ENABLED) - throw new RuntimeException("Unknown event"); + throw new RuntimeException("Unknown key code : " + event.getMKeyCode()); } } diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt index a73760205..98e93a64f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt @@ -58,7 +58,6 @@ object Defaults { const val PREF_VIBRATE_ON = false const val PREF_VIBRATE_IN_DND_MODE = false const val PREF_SOUND_ON = false - const val PREF_SHOW_EMOJI_DESCRIPTIONS = true @JvmField var PREF_POPUP_ON = true const val PREF_AUTO_CORRECTION = true diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index cc72c893c..ea6e8f59f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -67,7 +67,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_VIBRATE_IN_DND_MODE = "vibrate_in_dnd_mode"; public static final String PREF_SOUND_ON = "sound_on"; - public static final String PREF_SHOW_EMOJI_DESCRIPTIONS = "show_emoji_descriptions"; public static final String PREF_POPUP_ON = "popup_on"; public static final String PREF_AUTO_CORRECTION = "auto_correction"; public static final String PREF_MORE_AUTO_CORRECTION = "more_auto_correction"; diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index f750f1072..689a63700 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -54,7 +54,6 @@ public class SettingsValues { public final boolean mVibrateOn; public final boolean mVibrateInDndMode; public final boolean mSoundOn; - public final boolean mShowEmojiDescriptions; public final boolean mKeyPreviewPopupOn; public final boolean mShowsVoiceInputKey; public final boolean mLanguageSwitchKeyToOtherImes; @@ -170,7 +169,6 @@ public class SettingsValues { mVibrateOn = Settings.readVibrationEnabled(prefs); mVibrateInDndMode = prefs.getBoolean(Settings.PREF_VIBRATE_IN_DND_MODE, Defaults.PREF_VIBRATE_IN_DND_MODE); mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON); - mShowEmojiDescriptions = prefs.getBoolean(Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, Defaults.PREF_SHOW_EMOJI_DESCRIPTIONS); mKeyPreviewPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON, Defaults.PREF_POPUP_ON); mSlidingKeyInputPreviewEnabled = prefs.getBoolean( DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, Defaults.PREF_SLIDING_KEY_INPUT_PREVIEW); diff --git a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt index 05417fdb3..8715d2d4c 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt @@ -101,10 +101,6 @@ object DictionaryInfoUtils { return absoluteDirectoryName } - @JvmStatic - fun getCachedDictForLocaleAndType(locale: Locale, type: String, context: Context): File? = - getCachedDictsForLocale(locale, context).firstOrNull { it.name.substringBefore("_") == type } - fun getCachedDictsForLocale(locale: Locale, context: Context) = getCacheDirectoryForLocale(locale, context)?.let { File(it).listFiles() }.orEmpty() diff --git a/app/src/main/java/helium314/keyboard/latin/utils/IntentUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/IntentUtils.kt deleted file mode 100644 index da1b06bd8..000000000 --- a/app/src/main/java/helium314/keyboard/latin/utils/IntentUtils.kt +++ /dev/null @@ -1,26 +0,0 @@ -package helium314.keyboard.latin.utils - -import android.content.Context -import android.content.Intent -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.latin.inputlogic.InputLogic -import helium314.keyboard.latin.utils.Log.i - -object IntentUtils { - val TAG: String = InputLogic::class.java.simpleName - private val ACTION_SEND_INTENT = "helium314.keyboard.latin.ACTION_SEND_INTENT" - private val EXTRA_NUMBER = "EXTRA_NUMBER" - - @JvmStatic - fun handleSendIntentKey(context: Context, mKeyCode: Int) { - val intentNumber = (KeyCode.SEND_INTENT_ONE + 1) - mKeyCode; - - val intent: Intent = Intent(ACTION_SEND_INTENT).apply { - putExtra(EXTRA_NUMBER, intentNumber) - } - - context.sendBroadcast(intent) - i(TAG, "Sent broadcast for intent number: $intentNumber") - } -} - diff --git a/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt b/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt index 16b2e2d92..66ba49f64 100644 --- a/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/SearchScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -46,7 +45,6 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import helium314.keyboard.latin.R @@ -243,8 +241,7 @@ fun ExpandableSearchField( }) { CloseIcon(android.R.string.cancel) } }, singleLine = true, colors = colors, - textStyle = contentTextDirectionStyle, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search) + textStyle = contentTextDirectionStyle ) } } diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt index a82a07217..1405c71ae 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt @@ -47,12 +47,12 @@ fun NewDictionaryDialog( } else if (header != null) { val ctx = LocalContext.current val dictLocale = header.mLocaleString.constructLocale() + var locale by remember { mutableStateOf(mainLocale ?: dictLocale) } val enabledLanguages = SubtypeSettings.getEnabledSubtypes().map { it.locale().language } val comparer = compareBy({ it != mainLocale }, { it != dictLocale }, { it.language !in enabledLanguages }, { it.script() != dictLocale.script() }) val locales = SubtypeSettings.getAvailableSubtypeLocales() .filter { it.script() == dictLocale.script() || it.script() == mainLocale?.script() } .sortedWith(comparer) - var locale by remember { mutableStateOf(mainLocale ?: dictLocale.takeIf { it in locales } ?: locales.first()) } val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, ctx) val dictFile = File(cacheDir, header.mIdString.substringBefore(":") + "_" + DictionaryInfoUtils.USER_DICTIONARY_SUFFIX) val type = header.mIdString.substringBefore(":") diff --git a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt index c41696059..272757201 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt @@ -57,7 +57,6 @@ fun PreferencesScreen( Settings.PREF_SOUND_ON, if (prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON)) Settings.PREF_KEYPRESS_SOUND_VOLUME else null, - Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, R.string.settings_category_additional_keys, Settings.PREF_SHOW_NUMBER_ROW, if (SubtypeSettings.getEnabledSubtypes(true).any { it.locale().language in localesWithLocalizedNumberRow }) @@ -112,14 +111,6 @@ fun createPreferencesSettings(context: Context) = listOf( Setting(context, Settings.PREF_SOUND_ON, R.string.sound_on_keypress) { SwitchPreference(it, Defaults.PREF_SOUND_ON) }, - Setting( - context, Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, R.string.show_emoji_descriptions, - R.string.show_emoji_descriptions_summary - ) { - SwitchPreference(it, Defaults.PREF_SHOW_EMOJI_DESCRIPTIONS) { - KeyboardSwitcher.getInstance().reloadKeyboard() - } - }, Setting(context, Settings.PREF_ENABLE_CLIPBOARD_HISTORY, R.string.enable_clipboard_history, R.string.enable_clipboard_history_summary) { diff --git a/app/src/main/res/layout/popup_keys_keyboard.xml b/app/src/main/res/layout/popup_keys_keyboard.xml index e9e249482..a8bea57c4 100644 --- a/app/src/main/res/layout/popup_keys_keyboard.xml +++ b/app/src/main/res/layout/popup_keys_keyboard.xml @@ -10,15 +10,6 @@ android:layout_height="wrap_content" android:orientation="vertical" > - Vibrate in do not disturb mode Sound on keypress - - Show emoji description on long press - - Requires an emoji dictionary Popup on keypress diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml index 69994344c..0a8e99fc9 100644 --- a/app/src/main/res/xml/method.xml +++ b/app/src/main/res/xml/method.xml @@ -79,7 +79,6 @@ kn_IN: Kannada (India)/kannada kn_IN: Kannada Extended (India)/kannada ky: Kyrgyz/russian - la: Latin lg: Luganda/luganda # This is a preliminary keyboard layout. ln: Lingala/lingala # This is a preliminary keyboard layout. lo_LA: Lao (Laos)/lao @@ -829,15 +828,6 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:qwertz,AsciiCapable,EmojiCapable" /> - - diff --git a/layouts.md b/layouts.md index 59b092a9a..d03b20f98 100644 --- a/layouts.md +++ b/layouts.md @@ -73,7 +73,7 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm * `0.1` for phones * `0.09` for tablets * If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen -* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml#L250-L282) in the section _keyLabelFlags_ for names and numeric values +* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml#L251-L287) in the section _keyLabelFlags_ for names and numeric values * Since json does not support hexadecimal-values, you have to use the decimal values in the comments in the same line. * In case you want to apply multiple flags, you will need to combine them using [bitwise OR](https://en.wikipedia.org/wiki/Bitwise_operation#OR). In most cases this means you can just add the individual values, only exceptions are `fontDefault`, `followKeyLabelRatio`, `followKeyHintLabelRatio`, and `autoScale`. @@ -106,12 +106,6 @@ Usually the label is what is displayed on the key. However, there are some speci You can also specify special key codes like `a|!code/key_action_previous` or `abc|!code/-10043`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. * It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`. * You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt). You can also use toolbar key icons using the uppercase name of the [toolbar key](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. `!icon/redo` -* There are some further special labels to be used in popup keys (i.e. one of the popup keys should have the label) - * `!noPanelAutoPopupKey!`: no popups are shown, a long press will result in the first normal popup of the key being selected - * `!needsDividers!`: dividers are shown between popup keys - * `!hasLabels!`: reduces text size in popup keys for nicer display of labels instead of letters - * `!autoColumnOrder!`: use with a number, e.g. _!autoColumnOrder!4_ will result in 4 popup columns - * `!fixedColumnOrder!`: use with a number, e.g. _!fixedColumnOrder!4_ will result in 4 popup columns. Keys will not be re-ordered if the result is a single line. ## Adding new layouts / languages * You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts) diff --git a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt index d4f73733a..e8fe8f868 100644 --- a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt +++ b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/model/EmojiData.kt @@ -33,6 +33,36 @@ class EmojiData { } private fun onEmojiInserted(group: EmojiGroup, emoji: EmojiSpec): Boolean { + // Unicode RGI does not include letter symbols but Android supports them, so we inject them manually. + if (emoji.codes contentEquals RAW_CPS_KEYCAP_HASH) { + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A), 2.0f, "regional indicator symbol letter a") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_B), 2.0f, "regional indicator symbol letter b") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_C), 2.0f, "regional indicator symbol letter c") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_D), 2.0f, "regional indicator symbol letter d") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_E), 2.0f, "regional indicator symbol letter e") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_F), 2.0f, "regional indicator symbol letter f") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_G), 2.0f, "regional indicator symbol letter g") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_H), 2.0f, "regional indicator symbol letter h") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_I), 2.0f, "regional indicator symbol letter i") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_J), 2.0f, "regional indicator symbol letter j") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_K), 2.0f, "regional indicator symbol letter k") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_L), 2.0f, "regional indicator symbol letter l") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_M), 2.0f, "regional indicator symbol letter m") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_N), 2.0f, "regional indicator symbol letter n") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_O), 2.0f, "regional indicator symbol letter o") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_P), 2.0f, "regional indicator symbol letter p") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Q), 2.0f, "regional indicator symbol letter q") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_R), 2.0f, "regional indicator symbol letter r") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_S), 2.0f, "regional indicator symbol letter s") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_T), 2.0f, "regional indicator symbol letter t") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_U), 2.0f, "regional indicator symbol letter u") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_V), 2.0f, "regional indicator symbol letter v") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_W), 2.0f, "regional indicator symbol letter w") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_X), 2.0f, "regional indicator symbol letter x") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Y), 2.0f, "regional indicator symbol letter y") + insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z), 2.0f, "regional indicator symbol letter z") + } + // Some multi-skin-tone variants use a different base code than their non-multi-skin-tone counterparts, // so they don't get grouped. We drop them here, to prevent each variant from being displayed separately. return ! hasMultipleSkinModifiers(emoji.codes) @@ -88,6 +118,9 @@ class EmojiData { } companion object { + + private val RAW_CPS_KEYCAP_HASH = intArrayOf(0x0023, 0xFE0F, 0x20E3) + const val CP_NUL = 0x0000 private const val CP_ZWJ = 0x200D @@ -103,5 +136,34 @@ class EmojiData { private const val CP_WHITE_HAIR = 0x1F9B3 private const val CP_BARLD = 0x1F9B2 private const val CP_VARIANT_SELECTOR = 0xFE0F + + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A = 0x1F1E6 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_B = 0x1F1E7 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_C = 0x1F1E8 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_D = 0x1F1E9 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_E = 0x1F1EA + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_F = 0x1F1EB + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_G = 0x1F1EC + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_H = 0x1F1ED + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_I = 0x1F1EE + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_J = 0x1F1EF + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_K = 0x1F1F0 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_L = 0x1F1F1 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_M = 0x1F1F2 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_N = 0x1F1F3 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_O = 0x1F1F4 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_P = 0x1F1F5 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Q = 0x1F1F6 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_R = 0x1F1F7 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_S = 0x1F1F8 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_T = 0x1F1F9 + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_U = 0x1F1FA + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_V = 0x1F1FB + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_W = 0x1F1FC + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_X = 0x1F1FD + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Y = 0x1F1FE + private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z = 0x1F1FF } + + } diff --git a/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt b/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt index baaf17988..15aff89ab 100644 --- a/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt +++ b/tools/make-emoji-keys/src/main/resources/emoji/android-emoji-support.txt @@ -3866,5 +3866,31 @@ U+1F532 # black square button U+1F3C1 # chequered flag U+1F6A9 # triangular flag U+1F38C # crossed flags +U+1F1E6 # regional indicator symbol letter a +U+1F1E7 # regional indicator symbol letter b +U+1F1E8 # regional indicator symbol letter c +U+1F1E9 # regional indicator symbol letter d +U+1F1EA # regional indicator symbol letter e +U+1F1EB # regional indicator symbol letter f +U+1F1EC # regional indicator symbol letter g +U+1F1ED # regional indicator symbol letter h +U+1F1EE # regional indicator symbol letter i +U+1F1EF # regional indicator symbol letter j +U+1F1F0 # regional indicator symbol letter k +U+1F1F1 # regional indicator symbol letter l +U+1F1F2 # regional indicator symbol letter m +U+1F1F3 # regional indicator symbol letter n +U+1F1F4 # regional indicator symbol letter o +U+1F1F5 # regional indicator symbol letter p +U+1F1F6 # regional indicator symbol letter q +U+1F1F7 # regional indicator symbol letter r +U+1F1F8 # regional indicator symbol letter s +U+1F1F9 # regional indicator symbol letter t +U+1F1FA # regional indicator symbol letter u +U+1F1FB # regional indicator symbol letter v +U+1F1FC # regional indicator symbol letter w +U+1F1FD # regional indicator symbol letter x +U+1F1FE # regional indicator symbol letter y +U+1F1FF # regional indicator symbol letter z # Above emojis are supported from Android 4.4 (API level 19)