Compare commits

..

No commits in common. "main" and "v3.2" have entirely different histories.
main ... v3.2

47 changed files with 472 additions and 740 deletions

View file

@ -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 {

View file

@ -136,6 +136,32 @@
®️
™️
🫟
🇦
🇧
🇨
🇩
🇪
🇫
🇬
🇭
🇮
🇯
🇰
🇱
🇲
🇳
🇴
🇵
🇶
🇷
🇸
🇹
🇺
🇻
🇼
🇽
🇾
🇿
#️⃣
*️⃣
0
@ -221,4 +247,4 @@
💠
🔘
🔳
🔲
🔲

View file

@ -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.

View file

@ -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()
}
}

View file

@ -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) {}

View file

@ -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<HardwareEventDecoder> = 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
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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<Key> EMOJI_KEY_COMPARATOR = (lhs, rhs) -> {

View file

@ -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<EmojiPageKeyboardView> 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<Key, Keyboard> 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) {

View file

@ -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<EmojiPalettesAdapt
private static final boolean DEBUG_PAGER = false;
private final int mCategoryId;
private final EmojiViewCallback mEmojiViewCallback;
private final OnKeyEventListener mListener;
private final EmojiCategory mEmojiCategory;
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final EmojiViewCallback emojiViewCallback) {
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final OnKeyEventListener listener) {
mEmojiCategory = emojiCategory;
mCategoryId = categoryId;
mEmojiViewCallback = emojiViewCallback;
mListener = listener;
}
@NonNull
@ -37,7 +38,6 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
R.layout.emoji_keyboard_page, parent, false);
keyboardView.setEmojiViewCallback(mEmojiViewCallback);
return new ViewHolder(keyboardView);
}
@ -50,6 +50,7 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
final Keyboard keyboard =
mEmojiCategory.getKeyboardFromAdapterPosition(mCategoryId, position);
holder.getKeyboardView().setKeyboard(keyboard);
holder.getKeyboardView().setOnKeyEventListener(mListener);
}
@Override

View file

@ -38,16 +38,12 @@ import helium314.keyboard.keyboard.internal.KeyDrawParams;
import helium314.keyboard.keyboard.internal.KeyVisualAttributes;
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
import helium314.keyboard.latin.DictionaryFactory;
import helium314.keyboard.latin.R;
import helium314.keyboard.latin.RichInputMethodManager;
import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.SingleDictionaryFacilitator;
import helium314.keyboard.latin.common.ColorType;
import helium314.keyboard.latin.common.Colors;
import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.utils.DictionaryInfoUtils;
import helium314.keyboard.latin.utils.ResourceUtils;
import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
@ -64,7 +60,7 @@ import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
* Because of the above reasons, this class doesn't extend {@link KeyboardView}.
*/
public final class EmojiPalettesView extends LinearLayout
implements View.OnClickListener, EmojiViewCallback {
implements View.OnClickListener, OnKeyEventListener {
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
private long mCategoryId;
@ -183,14 +179,15 @@ public final class EmojiPalettesView extends LinearLayout
}
}
private static SingleDictionaryFacilitator sDictionaryFacilitator;
private boolean initialized = false;
private final Colors mColors;
private final EmojiLayoutParams mEmojiLayoutParams;
private LinearLayout mTabStrip;
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
private final EmojiCategory mEmojiCategory;
private ViewPager2 mPager;
@ -257,7 +254,9 @@ public final class EmojiPalettesView extends LinearLayout
mEmojiLayoutParams.setEmojiListProperties(mPager);
mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view);
mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true);
mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND));
initialized = true;
}
@ -281,7 +280,8 @@ public final class EmojiPalettesView extends LinearLayout
}
/**
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
* Called from {@link EmojiPageKeyboardView} through
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
* interface to handle touch events from non-View-based elements such as Emoji buttons.
*/
@Override
@ -291,9 +291,10 @@ public final class EmojiPalettesView extends LinearLayout
}
/**
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
* Called from {@link EmojiPageKeyboardView} through
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
* interface to handle touch events from non-View-based elements such as Emoji buttons.
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)}.
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)}.
*/
@Override
public void onReleaseKey(final Key key) {
@ -309,20 +310,6 @@ public final class EmojiPalettesView extends LinearLayout
mKeyboardActionListener.onCodeInput(KeyCode.ALPHA, NOT_A_COORDINATE, NOT_A_COORDINATE, false);
}
@Override
public String getDescription(String emoji) {
if (sDictionaryFacilitator == null) {
return null;
}
var wordProperty = sDictionaryFacilitator.getWordProperty(emoji);
if (wordProperty == null || ! wordProperty.mHasShortcuts) {
return null;
}
return wordProperty.mShortcutTargets.get(0).mWord;
}
public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
if (!enabled) return;
// TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
@ -337,7 +324,6 @@ public final class EmojiPalettesView extends LinearLayout
final KeyDrawParams params = new KeyDrawParams();
params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr);
setupSidePadding();
initDictionaryFacilitator();
}
private void addRecentKey(final Key key) {
@ -444,27 +430,5 @@ public final class EmojiPalettesView extends LinearLayout
mEmojiCategory.clearKeyboardCache();
mPager.getAdapter().notifyDataSetChanged();
closeDictionaryFacilitator();
}
private void initDictionaryFacilitator() {
if (Settings.getValues().mShowEmojiDescriptions) {
var locale = RichInputMethodManager.getInstance().getCurrentSubtype().getLocale();
if (sDictionaryFacilitator == null || ! sDictionaryFacilitator.isForLocale(locale)) {
closeDictionaryFacilitator();
var dictFile = DictionaryInfoUtils.getCachedDictForLocaleAndType(locale, "emoji", getContext());
var dictionary = dictFile != null? DictionaryFactory.getDictionary(dictFile, locale) : null;
sDictionaryFacilitator = dictionary != null? new SingleDictionaryFacilitator(dictionary) : null;
}
} else {
closeDictionaryFacilitator();
}
}
private static void closeDictionaryFacilitator() {
if (sDictionaryFacilitator != null) {
sDictionaryFacilitator.closeDictionaries();
sDictionaryFacilitator = null;
}
}
}

View file

@ -5,10 +5,10 @@ package helium314.keyboard.keyboard.emoji;
import helium314.keyboard.keyboard.Key;
/**
* Interface to handle callbacks from child elements
* such as Emoji buttons and keyboard views.
* Interface to handle touch events from non-View-based elements
* such as Emoji buttons.
*/
public interface EmojiViewCallback {
public interface OnKeyEventListener {
/**
* Called when a key is pressed by the user
@ -17,13 +17,8 @@ public interface EmojiViewCallback {
/**
* Called when a key is released.
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)},
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)},
* for example when a key from a popup keys keyboard is selected by releasing touch on it.
*/
void onReleaseKey(Key key);
/**
* Called from keyboard view to get an emoji description
*/
String getDescription(String emoji);
}

View file

@ -45,7 +45,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
"mns" -> 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<String>) {
@ -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<String> {
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<String> {
val tlds = linkedSetOf<String>()
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<String, String>(
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<String, String>(
"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"

View file

@ -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_<xxx>.
* Positive codes are passed through, unknown negative codes result in KeyEvent.KEYCODE_UNKNOWN.
* Convert a keyCode / codePoint to a KeyEvent.KEYCODE_<xxx>.
* 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_<xxx>.
* 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
}
}

View file

@ -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.

View file

@ -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
}

View file

@ -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<Dictionary>, 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) {

View file

@ -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;

View file

@ -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();

View file

@ -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<HardwareEventDecoder> 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);
}

View file

@ -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();

View file

@ -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) {

View file

@ -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<String, Boolean>) {}

View file

@ -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

View file

@ -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
}
}

View file

@ -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<Long> 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_<xxx>
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());
}
}

View file

@ -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

View file

@ -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";

View file

@ -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);

View file

@ -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()

View file

@ -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")
}
}

View file

@ -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
)
}
}

View file

@ -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<Locale>({ 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(":")

View file

@ -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)
{

View file

@ -10,15 +10,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
>
<helium314.keyboard.keyboard.PopupTextView
android:id="@+id/description_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:paddingBottom="4dp"
android:visibility="gone"
android:maxLines="1" android:ellipsize="end"
style="?attr/popupKeysKeyboardViewStyle" />
<helium314.keyboard.keyboard.PopupKeysKeyboardView
android:id="@+id/popup_keys_keyboard_view"
android:layout_width="wrap_content"

View file

@ -17,10 +17,6 @@
<string name="vibrate_in_dnd_mode">Vibrate in do not disturb mode</string>
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Sound on keypress</string>
<!-- Option to show emoji descriptions on long press -->
<string name="show_emoji_descriptions">Show emoji description on long press</string>
<!-- Description for option to show emoji descriptions on long press -->
<string name="show_emoji_descriptions_summary">Requires an emoji dictionary</string>
<!-- Option to control whether or not to show a popup with a larger font on each key press. -->
<string name="popup_on_keypress">Popup on keypress</string>
<!-- Settings screen title for preferences-->

View file

@ -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"
/>
<subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_generic"
android:subtypeId="0x37885a0c"
android:imeSubtypeLocale="la"
android:languageTag="la"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="true"
/>
<subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_generic"
android:subtypeId="0x1ec2b4c9"
@ -1053,7 +1043,7 @@
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:russian_extended,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false"
/>
<subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_generic_student"

View file

@ -92,9 +92,6 @@
<subtype
android:label="@string/subtype_generic"
android:subtypeLocale="kn" />
<subtype
android:label="@string/subtype_generic"
android:subtypeLocale="la" />
<subtype
android:label="@string/subtype_generic"
android:subtypeLocale="lb" />

View file

@ -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)

View file

@ -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
}
}

View file

@ -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)